Squashed 'third_party/pico-sdk/' content from commit 2062372d2

Change-Id: Ic20f199d3ed0ea8d3a6a1bbf513f875ec7500cc6
git-subtree-dir: third_party/pico-sdk
git-subtree-split: 2062372d203b372849d573f252cf7c6dc2800c0a
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..f66f9d0
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,46 @@
+function(_pico_init_pioasm)
+    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PICO_SDK_PATH}/tools)
+    # todo CMAKE_CURRENT_FUNCTION_LIST_DIR ... what version?
+    find_package(Pioasm REQUIRED)
+endfunction()
+
+function(pico_generate_pio_header TARGET PIO)
+    _pico_init_pioasm()
+    cmake_parse_arguments(pico_generate_pio_header "" "OUTPUT_DIR" "" ${ARGN} )
+
+    if (pico_generate_pio_header_OUTPUT_DIR)
+        get_filename_component(HEADER_DIR ${pico_generate_pio_header_OUTPUT_DIR} ABSOLUTE)
+    else()
+        set(HEADER_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
+    get_filename_component(PIO_NAME ${PIO} NAME)
+    set(HEADER "${HEADER_DIR}/${PIO_NAME}.h")
+    #message("Will generate ${HEADER}")
+    get_filename_component(HEADER_GEN_TARGET ${PIO} NAME_WE)
+    set(HEADER_GEN_TARGET "${TARGET}_${HEADER_GEN_TARGET}_pio_h")
+
+    add_custom_target(${HEADER_GEN_TARGET} DEPENDS ${HEADER})
+
+    add_custom_command(OUTPUT ${HEADER}
+            DEPENDS ${PIO}
+            COMMAND Pioasm -o c-sdk ${PIO} ${HEADER}
+            )
+    add_dependencies(${TARGET} ${HEADER_GEN_TARGET})
+    get_target_property(target_type ${TARGET} TYPE)
+    if ("INTERFACE_LIBRARY" STREQUAL "${target_type}")
+        target_include_directories(${TARGET} INTERFACE ${HEADER_DIR})
+    else()
+        target_include_directories(${TARGET} PUBLIC ${HEADER_DIR})
+    endif()
+endfunction()
+
+function(pico_add_uf2_output TARGET)
+    if (NOT ELF2UF2_FOUND)
+        set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PICO_SDK_PATH}/tools)
+        find_package(ELF2UF2)
+    endif()
+    if (ELF2UF2_FOUND)
+        add_custom_command(TARGET ${TARGET} POST_BUILD
+                COMMAND ELF2UF2 $<TARGET_FILE:${TARGET}> $<IF:$<BOOL:$<TARGET_PROPERTY:${TARGET},OUTPUT_NAME>>,$<TARGET_PROPERTY:${TARGET},OUTPUT_NAME>,$<TARGET_PROPERTY:${TARGET},NAME>>.uf2)
+    endif()
+endfunction()
diff --git a/tools/FindELF2UF2.cmake b/tools/FindELF2UF2.cmake
new file mode 100644
index 0000000..fb5dd97
--- /dev/null
+++ b/tools/FindELF2UF2.cmake
@@ -0,0 +1,43 @@
+# Finds (or builds) the ELF2UF2 executable
+#
+# This will define the following variables
+#
+#    ELF2UF2_FOUND
+#
+# and the following imported targets
+#
+#     ELF2UF2
+#
+
+if (NOT ELF2UF2_FOUND)
+    # todo we would like to use pckgconfig to look for it first
+    # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
+
+    include(ExternalProject)
+
+    set(ELF2UF2_SOURCE_DIR ${PICO_SDK_PATH}/tools/elf2uf2)
+    set(ELF2UF2_BINARY_DIR ${CMAKE_BINARY_DIR}/elf2uf2)
+
+    set(ELF2UF2_BUILD_TARGET ELF2UF2Build)
+    set(ELF2UF2_TARGET ELF2UF2)
+
+    if (NOT TARGET ${ELF2UF2_BUILD_TARGET})
+        pico_message_debug("ELF2UF2 will need to be built")
+        ExternalProject_Add(${ELF2UF2_BUILD_TARGET}
+                PREFIX elf2uf2 SOURCE_DIR ${ELF2UF2_SOURCE_DIR}
+                BINARY_DIR ${ELF2UF2_BINARY_DIR}
+                BUILD_ALWAYS 1 # force dependency checking
+                INSTALL_COMMAND ""
+                )
+    endif()
+
+    set(ELF2UF2_EXECUTABLE ${ELF2UF2_BINARY_DIR}/elf2uf2)
+    if(NOT TARGET ${ELF2UF2_TARGET})
+        add_executable(${ELF2UF2_TARGET} IMPORTED)
+    endif()
+    set_property(TARGET ${ELF2UF2_TARGET} PROPERTY IMPORTED_LOCATION
+            ${ELF2UF2_EXECUTABLE})
+
+    add_dependencies(${ELF2UF2_TARGET} ${ELF2UF2_BUILD_TARGET})
+    set(ELF2UF2_FOUND 1)
+endif()
diff --git a/tools/FindPioasm.cmake b/tools/FindPioasm.cmake
new file mode 100644
index 0000000..91b9021
--- /dev/null
+++ b/tools/FindPioasm.cmake
@@ -0,0 +1,50 @@
+# Finds (or builds) the Pioasm executable
+#
+# This will define the following variables
+#
+#    Pioasm_FOUND
+#
+# and the following imported targets
+#
+#     Pioasm
+#
+
+if (NOT Pioasm_FOUND)
+    # todo we would like to use pckgconfig to look for it first
+    # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
+
+    include(ExternalProject)
+
+    set(PIOASM_SOURCE_DIR ${PICO_SDK_PATH}/tools/pioasm)
+    set(PIOASM_BINARY_DIR ${CMAKE_BINARY_DIR}/pioasm)
+
+    set(PioasmBuild_TARGET PioasmBuild)
+    set(Pioasm_TARGET Pioasm)
+
+    if (NOT TARGET ${PioasmBuild_TARGET})
+        pico_message_debug("PIOASM will need to be built")
+#        message("Adding external project ${PioasmBuild_Target} in ${CMAKE_CURRENT_LIST_DIR}}")
+        ExternalProject_Add(${PioasmBuild_TARGET}
+                PREFIX pioasm SOURCE_DIR ${PIOASM_SOURCE_DIR}
+                BINARY_DIR ${PIOASM_BINARY_DIR}
+                BUILD_ALWAYS 1 # force dependency checking
+                INSTALL_COMMAND ""
+                )
+    endif()
+
+    if (CMAKE_HOST_WIN32)
+        set(Pioasm_EXECUTABLE ${PIOASM_BINARY_DIR}/pioasm.exe)
+    else()
+        set(Pioasm_EXECUTABLE ${PIOASM_BINARY_DIR}/pioasm)
+    endif()
+    if(NOT TARGET ${Pioasm_TARGET})
+#        message("Adding executable ${Pioasm_Target} in ${CMAKE_CURRENT_LIST_DIR}")
+        add_executable(${Pioasm_TARGET} IMPORTED)
+    endif()
+    set_property(TARGET ${Pioasm_TARGET} PROPERTY IMPORTED_LOCATION
+            ${Pioasm_EXECUTABLE})
+
+#    message("EXE is ${Pioasm_EXECUTABLE}")
+    add_dependencies(${Pioasm_TARGET} ${PioasmBuild_TARGET})
+    set(Pioasm_FOUND 1)
+endif()
diff --git a/tools/check_doxygen_groups.py b/tools/check_doxygen_groups.py
new file mode 100755
index 0000000..bbcd9f5
--- /dev/null
+++ b/tools/check_doxygen_groups.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#
+# Little script to check that every \ingroup has a matching \defgroup
+#
+# Usage:
+#
+# Run from the root of the tree to check
+
+
+import subprocess
+import re
+import sys
+import os
+
+groups = {}
+any_errors = False
+
+res = subprocess.run(['git', 'grep', '\\defgroup'], check=True, stdout=subprocess.PIPE)
+for line in res.stdout.decode('utf8').split('\n'):
+    m = re.match(r'^(\S+):.*\\defgroup\s+(\w+)', line)
+    if m:
+        filename = m.group(1)
+        group = m.group(2)
+        if os.path.basename(filename) in ('check_doxygen_groups.py', 'index.h'):
+            continue
+        if group in groups:
+            any_errors = True
+            print("{} uses \\defgroup {} but so does {}".format(groups[group], group, filename))
+        else:
+            groups[group] = filename
+
+res = subprocess.run(['git', 'grep', '\\ingroup'], check=True, stdout=subprocess.PIPE)
+for line in res.stdout.decode('utf8').split('\n'):
+    m = re.match(r'^(\S+):.*\\ingroup\s+(\w+)', line)
+    if m:
+        filename = m.group(1)
+        group = m.group(2)
+        if os.path.basename(filename) in ('check_doxygen_groups.py', 'index.h'):
+            continue
+        if group not in groups:
+            any_errors = True
+            print("{} uses \\ingroup {} which was never defined".format(filename, group))
+
+sys.exit(any_errors)
diff --git a/tools/elf2uf2/CMakeLists.txt b/tools/elf2uf2/CMakeLists.txt
new file mode 100644
index 0000000..070e114
--- /dev/null
+++ b/tools/elf2uf2/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.12)
+project(elf2uf2)
+
+set(CMAKE_CXX_STANDARD 14)
+
+add_subdirectory(../../src/common/boot_uf2 boot_uf2_headers)
+
+add_executable(elf2uf2 main.cpp)
+target_link_libraries(elf2uf2 boot_uf2_headers)
\ No newline at end of file
diff --git a/tools/elf2uf2/elf.h b/tools/elf2uf2/elf.h
new file mode 100644
index 0000000..32e3dbb
--- /dev/null
+++ b/tools/elf2uf2/elf.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _ELF_H
+#define _ELF_H
+
+#include <stdint.h>
+
+#define ELF_MAGIC 0x464c457fu
+
+#define EM_ARM 0x28u
+
+#define EF_ARM_ABI_FLOAT_HARD 0x00000400u
+
+#define PT_LOAD 0x00000001u
+
+#pragma pack(push, 1)
+struct elf_header {
+    uint32_t    magic;
+    uint8_t     arch_class;
+    uint8_t     endianness;
+    uint8_t     version;
+    uint8_t     abi;
+    uint8_t     abi_version;
+    uint8_t     _pad[7];
+    uint16_t    type;
+    uint16_t    machine;
+    uint32_t    version2;
+};
+
+struct elf32_header {
+    struct elf_header common;
+    uint32_t    entry;
+    uint32_t    ph_offset;
+    uint32_t    sh_offset;
+    uint32_t    flags;
+    uint16_t    eh_size;
+    uint16_t    ph_entry_size;
+    uint16_t    ph_num;
+    uint16_t    sh_entry_size;
+    uint16_t    sh_num;
+    uint16_t    sh_str_index;
+};
+
+struct elf32_ph_entry {
+    uint32_t type;
+    uint32_t offset;
+    uint32_t vaddr;
+    uint32_t paddr;
+    uint32_t filez;
+    uint32_t memsz;
+    uint32_t flags;
+    uint32_t align;
+};
+#pragma pack(pop)
+
+#endif
\ No newline at end of file
diff --git a/tools/elf2uf2/main.cpp b/tools/elf2uf2/main.cpp
new file mode 100644
index 0000000..2c0ddcd
--- /dev/null
+++ b/tools/elf2uf2/main.cpp
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstdio>
+#include <map>
+#include <vector>
+#include <cstring>
+#include <cstdarg>
+#include <algorithm>
+#include "boot/uf2.h"
+#include "elf.h"
+
+typedef unsigned int uint;
+
+#define ERROR_ARGS -1
+#define ERROR_FORMAT -2
+#define ERROR_INCOMPATIBLE -3
+#define ERROR_READ_FAILED -4
+#define ERROR_WRITE_FAILED -5
+
+static char error_msg[512];
+static bool verbose;
+
+static int fail(int code, const char *format, ...) {
+    va_list args;
+    va_start(args, format);
+    vsnprintf(error_msg, sizeof(error_msg), format, args);
+    va_end(args);
+    return code;
+}
+
+static int fail_read_error() {
+    return fail(ERROR_READ_FAILED, "Failed to read input file");
+}
+
+static int fail_write_error() {
+    return fail(ERROR_WRITE_FAILED, "Failed to write output file");
+}
+
+// we require 256 (as this is the page size supported by the device)
+#define LOG2_PAGE_SIZE 8u
+#define PAGE_SIZE (1u << LOG2_PAGE_SIZE)
+
+struct address_range {
+    enum type {
+        CONTENTS,     // may have contents
+        NO_CONTENTS,  // must be uninitialized
+        IGNORE        // will be ignored
+    };
+    address_range(uint32_t from, uint32_t to, type type) : from(from), to(to), type(type) {}
+    address_range() : address_range(0, 0, IGNORE) {}
+    type type;
+    uint32_t to;
+    uint32_t from;
+};
+
+typedef std::vector<address_range> address_ranges;
+
+#define MAIN_RAM_START        0x20000000u
+#define MAIN_RAM_END          0x20042000u
+#define FLASH_START           0x10000000u
+#define FLASH_END             0x15000000u
+#define XIP_SRAM_START        0x15000000u
+#define XIP_SRAM_END          0x15004000u
+#define MAIN_RAM_BANKED_START 0x21000000u
+#define MAIN_RAM_BANKED_END   0x21040000u
+
+const address_ranges rp2040_address_ranges_flash {
+    address_range(FLASH_START, FLASH_END, address_range::type::CONTENTS),
+    address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::NO_CONTENTS),
+    address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
+};
+
+const address_ranges rp2040_address_ranges_ram {
+    address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::CONTENTS),
+    address_range(XIP_SRAM_START, XIP_SRAM_END, address_range::type::CONTENTS),
+    address_range(0x00000000u, 0x00004000u, address_range::type::IGNORE) // for now we ignore the bootrom if present
+};
+
+struct page_fragment {
+    page_fragment(uint32_t file_offset, uint32_t page_offset, uint32_t bytes) : file_offset(file_offset), page_offset(page_offset), bytes(bytes) {}
+    uint32_t file_offset;
+    uint32_t page_offset;
+    uint32_t bytes;
+};
+
+static int usage() {
+    fprintf(stderr, "Usage: elf2uf2 (-v) <input ELF file> <output UF2 file>\n");
+    return ERROR_ARGS;
+}
+
+static int read_and_check_elf32_header(FILE *in, elf32_header& eh_out) {
+    if (1 != fread(&eh_out, sizeof(eh_out), 1, in)) {
+        return fail(ERROR_READ_FAILED, "Unable to read ELF header");
+    }
+    if (eh_out.common.magic != ELF_MAGIC) {
+        return fail(ERROR_FORMAT, "Not an ELF file");
+    }
+    if (eh_out.common.version != 1 || eh_out.common.version2 != 1) {
+        return fail(ERROR_FORMAT, "Unrecognized ELF version");
+    }
+    if (eh_out.common.arch_class != 1 || eh_out.common.endianness != 1) {
+        return fail(ERROR_INCOMPATIBLE, "Require 32 bit little-endian ELF");
+    }
+    if (eh_out.eh_size != sizeof(struct elf32_header)) {
+        return fail(ERROR_FORMAT, "Invalid ELF32 format");
+    }
+    if (eh_out.common.machine != EM_ARM) {
+        return fail(ERROR_FORMAT, "Not an ARM executable");
+    }
+    if (eh_out.common.abi != 0) {
+        return fail(ERROR_INCOMPATIBLE, "Unrecognized ABI");
+    }
+    if (eh_out.flags & EF_ARM_ABI_FLOAT_HARD) {
+        return fail(ERROR_INCOMPATIBLE, "HARD-FLOAT not supported");
+    }
+    return 0;
+}
+
+int check_address_range(const address_ranges& valid_ranges, uint32_t addr, uint32_t vaddr, uint32_t size, bool uninitialized, address_range &ar) {
+    for(const auto& range : valid_ranges) {
+        if (range.from <= addr && range.to >= addr + size) {
+            if (range.type == address_range::type::NO_CONTENTS && !uninitialized) {
+                return fail(ERROR_INCOMPATIBLE, "ELF contains memory contents for uninitialized memory");
+            }
+            ar = range;
+            if (verbose) {
+                printf("%s segment %08x->%08x (%08x->%08x)\n", uninitialized ? "Uninitialized" : "Mapped", addr,
+                   addr + size, vaddr, vaddr+size);
+            }
+            return 0;
+        }
+    }
+    return fail(ERROR_INCOMPATIBLE, "Memory segment %08x->%08x is outside of valid address range for device", addr, addr+size);
+}
+
+int read_and_check_elf32_ph_entries(FILE *in, const elf32_header &eh, const address_ranges& valid_ranges, std::map<uint32_t, std::vector<page_fragment>>& pages) {
+    if (eh.ph_entry_size != sizeof(elf32_ph_entry)) {
+        return fail(ERROR_FORMAT, "Invalid ELF32 program header");
+    }
+    if (eh.ph_num) {
+        std::vector<elf32_ph_entry> entries(eh.ph_num);
+        if (fseek(in, eh.ph_offset, SEEK_SET)) {
+            return fail_read_error();
+        }
+        if (eh.ph_num != fread(&entries[0], sizeof(struct elf32_ph_entry), eh.ph_num, in)) {
+            return fail_read_error();
+        }
+        for(uint i=0;i<eh.ph_num;i++) {
+            elf32_ph_entry& entry = entries[i];
+            if (entry.type == PT_LOAD && entry.memsz) {
+                address_range ar;
+                int rc;
+                uint mapped_size = std::min(entry.filez, entry.memsz);
+                if (mapped_size) {
+                    rc = check_address_range(valid_ranges, entry.paddr, entry.vaddr, mapped_size, false, ar);
+                    if (rc) return rc;
+                    // we don't download uninitialized, generally it is BSS and should be zero-ed by crt0.S, or it may be COPY areas which are undefined
+                    if (ar.type != address_range::type::CONTENTS) {
+                        if (verbose) printf("  ignored\n");
+                        continue;
+                    }
+                    uint addr = entry.paddr;
+                    uint remaining = mapped_size;
+                    uint file_offset = entry.offset;
+                    while (remaining) {
+                        uint off = addr & (PAGE_SIZE - 1);
+                        uint len = std::min(remaining, PAGE_SIZE - off);
+                        auto &fragments = pages[addr - off]; // list of fragments
+                        // note if filesz is zero, we want zero init which is handled because the
+                        // statement above creates an empty page fragment list
+                        // check overlap with any existing fragments
+                        for (const auto &fragment : fragments) {
+                            if ((off < fragment.page_offset + fragment.bytes) !=
+                                ((off + len) <= fragment.page_offset)) {
+                                fail(ERROR_FORMAT, "In memory segments overlap");
+                            }
+                        }
+                        fragments.push_back(
+                                page_fragment{file_offset,off,len});
+                        addr += len;
+                        file_offset += len;
+                        remaining -= len;
+                    }
+                }
+                if (entry.memsz > entry.filez) {
+                    // we have some uninitialized data too
+                    rc = check_address_range(valid_ranges, entry.paddr + entry.filez, entry.vaddr + entry.filez, entry.memsz - entry.filez, true,
+                                             ar);
+                    if (rc) return rc;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+int realize_page(FILE *in, const std::vector<page_fragment> &fragments, uint8_t *buf, uint buf_len) {
+    assert(buf_len >= PAGE_SIZE);
+    for(auto& frag : fragments) {
+        assert(frag.page_offset >= 0 && frag.page_offset < PAGE_SIZE && frag.page_offset + frag.bytes <= PAGE_SIZE);
+        if (fseek(in, frag.file_offset, SEEK_SET)) {
+            return fail_read_error();
+        }
+        if (1 != fread(buf + frag.page_offset, frag.bytes, 1, in)) {
+            return fail_read_error();
+        }
+    }
+    return 0;
+}
+
+static bool is_address_valid(const address_ranges& valid_ranges, uint32_t addr) {
+    for(const auto& range : valid_ranges) {
+        if (range.from <= addr && range.to > addr) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool is_address_initialized(const address_ranges& valid_ranges, uint32_t addr) {
+    for(const auto& range : valid_ranges) {
+        if (range.from <= addr && range.to > addr) {
+            return address_range::type::CONTENTS == range.type;
+        }
+    }
+    return false;
+}
+
+static bool is_address_mapped(const std::map<uint32_t, std::vector<page_fragment>>& pages, uint32_t addr) {
+    uint32_t page = addr & ~(PAGE_SIZE - 1);
+    if (!pages.count(page)) return false;
+    // todo check actual address within page
+    return true;
+}
+
+int elf2uf2(FILE *in, FILE *out) {
+    elf32_header eh;
+    std::map<uint32_t, std::vector<page_fragment>> pages;
+    int rc = read_and_check_elf32_header(in, eh);
+    bool ram_style = false;
+    address_ranges valid_ranges = {};
+    if (!rc) {
+        ram_style = is_address_initialized(rp2040_address_ranges_ram, eh.entry);
+        if (verbose) {
+            if (ram_style) {
+                printf("Detected RAM binary\n");
+            } else {
+                printf("Detected FLASH binary\n");
+            }
+        }
+        valid_ranges = ram_style ? rp2040_address_ranges_ram : rp2040_address_ranges_flash;
+        rc = read_and_check_elf32_ph_entries(in, eh, valid_ranges, pages);
+    }
+    if (rc) return rc;
+    if (pages.empty()) {
+        return fail(ERROR_INCOMPATIBLE, "The input file has no memory pages");
+    }
+    uint page_num = 0;
+    if (ram_style) {
+        uint32_t expected_ep_main_ram = UINT32_MAX;
+        uint32_t expected_ep_xip_sram = UINT32_MAX;
+        for(auto& page_entry : pages) {
+            if ( ((page_entry.first >= MAIN_RAM_START) && (page_entry.first < MAIN_RAM_END)) && (page_entry.first < expected_ep_main_ram) ) {
+                expected_ep_main_ram = page_entry.first | 0x1;
+            } else if ( ((page_entry.first >= XIP_SRAM_START) && (page_entry.first < XIP_SRAM_END)) && (page_entry.first < expected_ep_xip_sram) ) { 
+                expected_ep_xip_sram = page_entry.first | 0x1;
+            }
+        }
+        uint32_t expected_ep = (UINT32_MAX != expected_ep_main_ram) ? expected_ep_main_ram : expected_ep_xip_sram;
+        if (eh.entry == expected_ep_xip_sram) {
+            return fail(ERROR_INCOMPATIBLE, "B0/B1 Boot ROM does not support direct entry into XIP_SRAM\n");
+        } else if (eh.entry != expected_ep) {
+            return fail(ERROR_INCOMPATIBLE, "A RAM binary should have an entry point at the beginning: %08x (not %08x)\n", expected_ep, eh.entry);
+        }
+        static_assert(0 == (MAIN_RAM_START & (PAGE_SIZE - 1)), "");
+        // currently don't require this as entry point is now at the start, we don't know where reset vector is
+#if 0
+        uint8_t buf[PAGE_SIZE];
+        rc = realize_page(in, pages[MAIN_RAM_START], buf, sizeof(buf));
+        if (rc) return rc;
+        uint32_t sp = ((uint32_t *)buf)[0];
+        uint32_t ip = ((uint32_t *)buf)[1];
+        if (!is_address_mapped(pages, ip)) {
+            return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: reset vector %08x is not in mapped memory",
+                MAIN_RAM_START, ip);
+        }
+        if (!is_address_valid(valid_ranges, sp - 4)) {
+            return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: stack pointer %08x is not in RAM",
+                        MAIN_RAM_START, sp);
+        }
+#endif
+    }
+    uf2_block block;
+    block.magic_start0 = UF2_MAGIC_START0;
+    block.magic_start1 = UF2_MAGIC_START1;
+    block.flags = UF2_FLAG_FAMILY_ID_PRESENT;
+    block.payload_size = PAGE_SIZE;
+    block.num_blocks = (uint32_t)pages.size();
+    block.file_size = RP2040_FAMILY_ID;
+    block.magic_end = UF2_MAGIC_END;
+    for(auto& page_entry : pages) {
+        block.target_addr = page_entry.first;
+        block.block_no = page_num++;
+        if (verbose) {
+            printf("Page %d / %d %08x\n", block.block_no, block.num_blocks, block.target_addr);
+        }
+        memset(block.data, 0, sizeof(block.data));
+        rc = realize_page(in, page_entry.second, block.data, sizeof(block.data));
+        if (rc) return rc;
+        if (1 != fwrite(&block, sizeof(uf2_block), 1, out)) {
+            return fail_write_error();
+        }
+    }
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    int arg = 1;
+    if (arg < argc && !strcmp(argv[arg], "-v")) {
+        verbose = true;
+        arg++;
+    }
+    if (argc < arg + 2) {
+        return usage();
+    }
+    const char *in_filename = argv[arg++];
+    FILE *in = fopen(in_filename, "rb");
+    if (!in) {
+        fprintf(stderr, "Can't open input file '%s'\n", in_filename);
+        return ERROR_ARGS;
+    }
+    const char *out_filename = argv[arg++];
+    FILE *out = fopen(out_filename, "wb");
+    if (!out) {
+        fprintf(stderr, "Can't open output file '%s'\n", out_filename);
+        return ERROR_ARGS;
+    }
+
+    int rc = elf2uf2(in, out);
+    fclose(in);
+    fclose(out);
+    if (rc) {
+        remove(out_filename);
+        if (error_msg[0]) {
+            fprintf(stderr, "ERROR: %s\n", error_msg);
+        }
+    }
+    return rc;
+}
diff --git a/tools/extract_configs.py b/tools/extract_configs.py
new file mode 100755
index 0000000..e45ae7c
--- /dev/null
+++ b/tools/extract_configs.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#
+# Script to scan the Raspberry Pi Pico SDK tree searching for configuration items
+# Outputs a tab separated file of the configuration item:
+# name	location	description	type	advanced	default	depends	enumvalues	group	max	min
+#
+# Usage:
+#
+# ./extract_configs.py <root of source tree> [output file]
+#
+# If not specified, output file will be `pico_configs.tsv`
+
+
+import os
+import sys
+import re
+import csv
+import logging
+
+logger = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+scandir = sys.argv[1]
+outfile = sys.argv[2] if len(sys.argv) > 2 else 'pico_configs.tsv'
+
+CONFIG_RE = re.compile(r'//\s+PICO_CONFIG:\s+(\w+),\s+([^,]+)(?:,\s+(.*))?$')
+DEFINE_RE = re.compile(r'#define\s+(\w+)\s+(.+?)(\s*///.*)?$')
+
+all_configs = {}
+all_attrs = set()
+all_descriptions = {}
+all_defines = {}
+
+
+
+def ValidateAttrs(config_attrs):
+    _type = config_attrs.get('type', 'int')
+
+    # Validate attrs
+    if _type == 'int':
+        assert 'enumvalues' not in config_attrs
+        _min = _max = _default = None
+        if config_attrs.get('min', None) is not None:
+            value = config_attrs['min']
+            m = re.match(r'^(\d+)e(\d+)$', value.lower())
+            if m:
+                _min = int(m.group(1)) * 10**int(m.group(2))
+            else:
+                _min = int(value, 0)
+        if config_attrs.get('max', None) is not None:
+            value = config_attrs['max']
+            m = re.match(r'^(\d+)e(\d+)$', value.lower())
+            if m:
+                _max = int(m.group(1)) * 10**int(m.group(2))
+            else:
+                _max = int(value, 0)
+        if config_attrs.get('default', None) is not None:
+            if '/' not in config_attrs['default']:
+                try:
+                    value = config_attrs['default']
+                    m = re.match(r'^(\d+)e(\d+)$', value.lower())
+                    if m:
+                        _default = int(m.group(1)) * 10**int(m.group(2))
+                    else:
+                        _default = int(value, 0)
+                except ValueError:
+                    pass
+        if _min is not None and _max is not None:
+            if _min > _max:
+                raise Exception('{} at {}:{} has min {} > max {}'.format(config_name, file_path, linenum, config_attrs['min'], config_attrs['max']))
+        if _min is not None and _default is not None:
+            if _min > _default:
+                raise Exception('{} at {}:{} has min {} > default {}'.format(config_name, file_path, linenum, config_attrs['min'], config_attrs['default']))
+        if _default is not None and _max is not None:
+            if _default > _max:
+                raise Exception('{} at {}:{} has default {} > max {}'.format(config_name, file_path, linenum, config_attrs['default'], config_attrs['max']))
+    elif _type == 'bool':
+
+        assert 'min' not in config_attrs
+        assert 'max' not in config_attrs
+        assert 'enumvalues' not in config_attrs
+
+        _default = config_attrs.get('default', None)
+        if _default is not None:
+            if '/' not in _default:
+                if (_default.lower() != '0') and (config_attrs['default'].lower() != '1') and ( _default not in all_configs):
+                    logger.info('{} at {}:{} has non-integer default value "{}"'.format(config_name, file_path, linenum, config_attrs['default']))
+
+    elif _type == 'enum':
+
+        assert 'min' not in config_attrs
+        assert 'max' not in config_attrs
+        assert 'enumvalues' in config_attrs
+
+        _enumvalues = tuple(config_attrs['enumvalues'].split('|'))
+        _default = None
+        if config_attrs.get('default', None) is not None:
+            _default = config_attrs['default']
+        if _default is not None:
+            if _default not in _enumvalues:
+                raise Exception('{} at {}:{} has default value {} which isn\'t in list of enumvalues {}'.format(config_name, file_path, linenum, config_attrs['default'], config_attrs['enumvalues']))
+    else:
+        raise Exception("Found unknown PICO_CONFIG type {} at {}:{}".format(_type, file_path, linenum))
+
+
+
+
+# Scan all .c and .h files in the specific path, recursively.
+
+for dirpath, dirnames, filenames in os.walk(scandir):
+    for filename in filenames:
+        file_ext = os.path.splitext(filename)[1]
+        if file_ext in ('.c', '.h'):
+            file_path = os.path.join(dirpath, filename)
+
+            with open(file_path, encoding="ISO-8859-1") as fh:
+                linenum = 0
+                for line in fh.readlines():
+                    linenum += 1
+                    line = line.strip()
+                    m = CONFIG_RE.match(line)
+                    if m:
+                        config_name = m.group(1)
+                        config_description = m.group(2)
+                        _attrs = m.group(3)
+                        # allow commas to appear inside brackets by converting them to and from NULL chars
+                        _attrs = re.sub(r'(\(.+\))', lambda m: m.group(1).replace(',', '\0'), _attrs)
+
+                        if '=' in config_description:
+                            raise Exception("For {} at {}:{} the description was set to '{}' - has the description field been omitted?".format(config_name, file_path, linenum, config_description))
+                        if config_description in all_descriptions:
+                            raise Exception("Found description {} at {}:{} but it was already used at {}:{}".format(config_description, file_path, linenum, os.path.join(scandir, all_descriptions[config_description]['filename']), all_descriptions[config_description]['line_number']))
+                        else:
+                            all_descriptions[config_description] = {'config_name': config_name, 'filename': os.path.relpath(file_path, scandir), 'line_number': linenum}
+
+                        config_attrs = {}
+                        prev = None
+                        # Handle case where attr value contains a comma
+                        for item in _attrs.split(','):
+                            if "=" not in item:
+                                assert(prev)
+                                item = prev + "," + item
+                            try:
+                                k, v = (i.strip() for i in item.split('='))
+                            except ValueError:
+                                raise Exception('{} at {}:{} has malformed value {}'.format(config_name, file_path, linenum, item))
+                            config_attrs[k] = v.replace('\0', ',')
+                            all_attrs.add(k)
+                            prev = item
+                        #print(file_path, config_name, config_attrs)
+
+                        if 'group' not in config_attrs:
+                            raise Exception('{} at {}:{} has no group attribute'.format(config_name, file_path, linenum))
+
+                        #print(file_path, config_name, config_attrs)
+                        if config_name in all_configs:
+                            raise Exception("Found {} at {}:{} but it was already declared at {}:{}".format(config_name, file_path, linenum, os.path.join(scandir, all_configs[config_name]['filename']), all_configs[config_name]['line_number']))
+                        else:
+                            all_configs[config_name] = {'attrs': config_attrs, 'filename': os.path.relpath(file_path, scandir), 'line_number': linenum, 'description': config_description}
+                    else:
+                        m = DEFINE_RE.match(line)
+                        if m:
+                            name = m.group(1)
+                            value = m.group(2)
+                            # discard any 'u' qualifier
+                            m = re.match(r'^((0x)?\d+)u$', value.lower())
+                            if m:
+                                value = m.group(1)
+                            else:
+                                # discard any '_u(X)' macro
+                                m = re.match(r'^_u\(((0x)?\d+)\)$', value.lower())
+                                if m:
+                                    value = m.group(1)
+                            if name not in all_defines:
+                                all_defines[name] = dict()
+                            if value not in all_defines[name]:
+                                all_defines[name][value] = set()
+                            all_defines[name][value] = (file_path, linenum)
+
+# Check for defines with missing PICO_CONFIG entries
+resolved_defines = dict()
+for d in all_defines:
+    if d not in all_configs and d.startswith("PICO_"):
+        logger.warning("Potential unmarked PICO define {}".format(d))
+    # resolve "nested defines" - this allows e.g. USB_DPRAM_MAX to resolve to USB_DPRAM_SIZE which is set to 4096 (which then matches the relevant PICO_CONFIG entry)
+    for val in all_defines[d]:
+        if val in all_defines:
+            resolved_defines[d] = all_defines[val]
+
+for config_name in all_configs:
+
+    ValidateAttrs(all_configs[config_name]['attrs'])
+
+    # Check that default values match up
+    if 'default' in all_configs[config_name]['attrs']:
+        if config_name in all_defines:
+            if all_configs[config_name]['attrs']['default'] not in all_defines[config_name] and (config_name not in resolved_defines or all_configs[config_name]['attrs']['default'] not in resolved_defines[config_name]):
+                if '/' in all_configs[config_name]['attrs']['default'] or ' ' in all_configs[config_name]['attrs']['default']:
+                    continue
+                # There _may_ be multiple matching defines, but arbitrarily display just one in the error message
+                first_define_value = list(all_defines[config_name].keys())[0]
+                raise Exception('Found {} at {}:{} with a default of {}, but #define says {} (at {}:{})'.format(config_name, os.path.join(scandir, all_configs[config_name]['filename']), all_configs[config_name]['line_number'], all_configs[config_name]['attrs']['default'], first_define_value, all_defines[config_name][first_define_value][0], all_defines[config_name][first_define_value][1]))
+        else:
+            raise Exception('Found {} at {}:{} with a default of {}, but no matching #define found'.format(config_name, os.path.join(scandir, all_configs[config_name]['filename']), all_configs[config_name]['line_number'], all_configs[config_name]['attrs']['default']))
+
+with open(outfile, 'w', newline='') as csvfile:
+    fieldnames = ('name', 'location', 'description', 'type') + tuple(sorted(all_attrs - set(['type'])))
+    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore', dialect='excel-tab')
+
+    writer.writeheader()
+    for config_name in sorted(all_configs):
+        writer.writerow({'name': config_name, 'location': '{}:{}'.format(all_configs[config_name]['filename'], all_configs[config_name]['line_number']), 'description': all_configs[config_name]['description'], **all_configs[config_name]['attrs']})
diff --git a/tools/pioasm/CMakeLists.txt b/tools/pioasm/CMakeLists.txt
new file mode 100644
index 0000000..df80530
--- /dev/null
+++ b/tools/pioasm/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.4)
+project(pioasm CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+
+if (PIOASM_GENERATE_PARSER)
+    find_package(BISON 3.4.2)
+    find_package(FLEX 2.5.13) # no idea about the version
+
+    FLEX_TARGET(pioasm_lexer lexer.ll ${CMAKE_CURRENT_SOURCE_DIR}/gen/lexer.cpp)
+    BISON_TARGET(pioasm_parser parser.yy ${CMAKE_CURRENT_SOURCE_DIR}/gen/parser.cpp COMPILE_FLAGS "-Wcounterexamples")
+    ADD_FLEX_BISON_DEPENDENCY(pioasm_lexer pioasm_parser)
+endif()
+
+add_executable(pioasm
+        main.cpp
+        pio_assembler.cpp
+        pio_disassembler.cpp
+        gen/lexer.cpp
+        gen/parser.cpp
+)
+
+target_sources(pioasm PRIVATE c_sdk_output.cpp)
+target_sources(pioasm PRIVATE python_output.cpp)
+target_sources(pioasm PRIVATE hex_output.cpp)
+target_sources(pioasm PRIVATE ada_output.cpp)
+target_sources(pioasm PRIVATE ${PIOASM_EXTRA_SOURCE_FILES})
+
+if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND
+    (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "7") AND
+    (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9") AND
+    (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm.*$"))
+    # disable GCC ARM info notice about ABI change
+    target_compile_options(pioasm PRIVATE -Wno-psabi)
+endif()
+
+target_include_directories(pioasm PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/gen)
+
+if (MSVC)
+    target_compile_definitions(pioasm PRIVATE YY_NO_UNISTD_H)
+    target_compile_options(pioasm PRIVATE "/std:c++latest")
+endif()
+
diff --git a/tools/pioasm/ada_output.cpp b/tools/pioasm/ada_output.cpp
new file mode 100644
index 0000000..8598f33
--- /dev/null
+++ b/tools/pioasm/ada_output.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Ada specifications generated by this assembler depend on the RP.PIO package,
+ * available in rp2040_hal.
+ *
+ * https://github.com/JeremyGrosser/rp2040_hal
+ * https://github.com/JeremyGrosser/pico_bsp
+ * https://github.com/JeremyGrosser/pico_examples
+ */
+
+#include <algorithm>
+#include <iostream>
+#include "output_format.h"
+#include "pio_disassembler.h"
+
+struct ada_output : public output_format {
+    struct factory {
+        factory() {
+            output_format::add(new ada_output());
+        }
+    };
+
+    ada_output() : output_format("ada") {}
+
+    std::string get_description() override {
+        return "Ada specification";
+    }
+
+    void output_symbols(FILE *out, const std::vector<compiled_source::symbol> &symbols) {
+        int count = 0;
+        for (const auto &s : symbols) {
+            if (!s.is_label) {
+                fprintf(out, "%s : constant := %d;\n", s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+            count = 0;
+        }
+        for (const auto &s : symbols) {
+            if (s.is_label) {
+                fprintf(out, "   Offset_%s : constant := %d;\n", s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+        }
+    }
+
+    void ada_case(std::string &identifier) {
+        for(std::string::size_type i = 0; i < identifier.size(); ++i) {
+            if ((i == 0) || (identifier[i - 1] == '_')) {
+                identifier[i] = toupper(identifier[i]);
+            }
+        }
+    }
+
+    void header(FILE *out, const std::string msg, const int indent) {
+        const std::string dashes = std::string(msg.length() + 6, '-');
+        const std::string indent_str= std::string(indent, ' ');
+        fprintf(out, "%s%s\n", indent_str.c_str(), dashes.c_str());
+        fprintf(out, "%s-- %s --\n", indent_str.c_str(), msg.c_str());
+        fprintf(out, "%s%s\n", indent_str.c_str(), dashes.c_str());
+        fprintf(out, "\n");
+    }
+
+    int output(std::string destination, std::vector<std::string> output_options,
+               const compiled_source &source) override {
+
+        for (const auto &program : source.programs) {
+            for(const auto &p : program.lang_opts) {
+                if (p.first.size() >= name.size() && p.first.compare(0, name.size(), name) == 0) {
+                    std::cerr << "warning: " << name << " does not support output options; " << p.first << " lang_opt ignored.\n";
+                }
+            }
+        }
+
+        std::string package_name;
+
+        switch (output_options.size()) {
+            case 0:
+                std::cerr << "error: missing package name options for Ada format" << std::endl;
+                return 1;
+            case 1:
+                package_name = output_options[0]; // Package name from command options
+                break;
+            default:
+                std::cerr << "error: too many options for Ada format" << std::endl;
+                return 1;
+        }
+
+        FILE *out = open_single_output(destination);
+        if (!out) return 1;
+
+        header(out, "This file is autogenerated by pioasm; do not edit!", 0);
+        fprintf(out, "pragma Style_Checks (Off);\n\n");
+        fprintf(out, "with RP.PIO;\n\n");
+
+        fprintf(out, "package %s is\n", package_name.c_str());
+
+        for (const auto &program : source.programs) {
+            std::string trailing_comma = ", ";
+
+            std::string prog_name= program.name;
+            ada_case(prog_name);
+
+            fprintf(out, "\n");
+            header(out, prog_name, 3);
+
+
+            output_symbols(out, source.global_symbols);
+            fprintf(out, "   %s_Wrap_Target : constant := %d;\n", prog_name.c_str(), program.wrap_target);
+            fprintf(out, "   %s_Wrap        : constant := %d;\n", prog_name.c_str(), program.wrap);
+            fprintf(out, "\n");
+
+            output_symbols(out, program.symbols);
+
+            fprintf(out, "   %s_Program_Instructions : RP.PIO.Program := (\n", prog_name.c_str());
+            for (int i = 0; i < (int)program.instructions.size(); i++) {
+                const auto &inst = program.instructions[i];
+                if (i == program.wrap_target) {
+                    fprintf(out, "                    --  .wrap_target\n");
+                }
+                if (i == (int)program.instructions.size() - 1) {
+                    trailing_comma = ");";
+                }
+                fprintf(out, "         16#%04x#%s --  %2d: %s\n", inst, trailing_comma.c_str(), i,
+                        disassemble(inst, program.sideset_bits_including_opt.get(), program.sideset_opt).c_str());
+                if (i == program.wrap) {
+                    fprintf(out, "                    --  .wrap\n");
+                }
+            }
+        }
+        fprintf(out, "\n");
+        fprintf(out, "end %s;\n", package_name.c_str());
+        fclose(out);
+        return 0;
+    }
+};
+
+static ada_output::factory creator;
diff --git a/tools/pioasm/c_sdk_output.cpp b/tools/pioasm/c_sdk_output.cpp
new file mode 100644
index 0000000..9388c0d
--- /dev/null
+++ b/tools/pioasm/c_sdk_output.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <algorithm>
+#include <iostream>
+#include "output_format.h"
+#include "pio_disassembler.h"
+
+struct c_sdk_output : public output_format {
+    struct factory {
+        factory() {
+            output_format::add(new c_sdk_output());
+        }
+    };
+
+    c_sdk_output() : output_format("c-sdk") {}
+
+    std::string get_description() override {
+        return "C header suitable for use with the Raspberry Pi Pico SDK";
+    }
+
+    void output_symbols(FILE *out, std::string prefix, const std::vector<compiled_source::symbol> &symbols) {
+        int count = 0;
+        for (const auto &s : symbols) {
+            if (!s.is_label) {
+                fprintf(out, "#define %s%s %d\n", prefix.c_str(), s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+            count = 0;
+        }
+        for (const auto &s : symbols) {
+            if (s.is_label) {
+                fprintf(out, "#define %soffset_%s %du\n", prefix.c_str(), s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+        }
+    }
+
+    void header(FILE *out, std::string msg) {
+        std::string dashes = std::string(msg.length(), '-');
+        fprintf(out, "// %s //\n", dashes.c_str());
+        fprintf(out, "// %s //\n", msg.c_str());
+        fprintf(out, "// %s //\n", dashes.c_str());
+        fprintf(out, "\n");
+    }
+
+    int output(std::string destination, std::vector<std::string> output_options,
+               const compiled_source &source) override {
+
+        for (const auto &program : source.programs) {
+            for(const auto &p : program.lang_opts) {
+                if (p.first.size() >= name.size() && p.first.compare(0, name.size(), name) == 0) {
+                    std::cerr << "warning: " << name << " does not support output options; " << p.first << " lang_opt ignored.\n";
+                }
+            }
+        }
+        FILE *out = open_single_output(destination);
+        if (!out) return 1;
+
+        header(out, "This file is autogenerated by pioasm; do not edit!");
+
+        fprintf(out, "#pragma once\n");
+        fprintf(out, "\n");
+        fprintf(out, "#if !PICO_NO_HARDWARE\n");
+        fprintf(out, "#include \"hardware/pio.h\"\n");
+        fprintf(out, "#endif\n");
+        fprintf(out, "\n");
+
+        output_symbols(out, "", source.global_symbols);
+
+        for (const auto &program : source.programs) {
+            header(out, program.name);
+
+            std::string prefix = program.name + "_";
+
+            fprintf(out, "#define %swrap_target %d\n", prefix.c_str(), program.wrap_target);
+            fprintf(out, "#define %swrap %d\n", prefix.c_str(), program.wrap);
+            fprintf(out, "\n");
+
+            output_symbols(out, prefix, program.symbols);
+
+            fprintf(out, "static const uint16_t %sprogram_instructions[] = {\n", prefix.c_str());
+            for (int i = 0; i < (int)program.instructions.size(); i++) {
+                const auto &inst = program.instructions[i];
+                if (i == program.wrap_target) {
+                    fprintf(out, "            //     .wrap_target\n");
+                }
+                fprintf(out, "    0x%04x, // %2d: %s\n", inst, i,
+                        disassemble(inst, program.sideset_bits_including_opt.get(), program.sideset_opt).c_str());
+                if (i == program.wrap) {
+                    fprintf(out, "            //     .wrap\n");
+                }
+            }
+            fprintf(out, "};\n");
+            fprintf(out, "\n");
+
+            fprintf(out, "#if !PICO_NO_HARDWARE\n");
+            fprintf(out, "static const struct pio_program %sprogram = {\n", prefix.c_str());
+            fprintf(out, "    .instructions = %sprogram_instructions,\n", prefix.c_str());
+            fprintf(out, "    .length = %d,\n", (int) program.instructions.size());
+            fprintf(out, "    .origin = %d,\n", program.origin.get());
+            fprintf(out, "};\n");
+            fprintf(out, "\n");
+            fprintf(out, "static inline pio_sm_config %sprogram_get_default_config(uint offset) {\n", prefix.c_str());
+            fprintf(out, "    pio_sm_config c = pio_get_default_sm_config();\n");
+            fprintf(out, "    sm_config_set_wrap(&c, offset + %swrap_target, offset + %swrap);\n", prefix.c_str(),
+                    prefix.c_str());
+            if (program.sideset_bits_including_opt.is_specified()) {
+                fprintf(out, "    sm_config_set_sideset(&c, %d, %s, %s);\n", program.sideset_bits_including_opt.get(),
+                        program.sideset_opt ? "true" : "false",
+                        program.sideset_pindirs ? "true" : "false");
+            }
+            fprintf(out, "    return c;\n");
+            fprintf(out, "}\n");
+
+            // todo maybe have some code blocks inside or outside here?
+            for(const auto& o : program.code_blocks) {
+                fprintf(out, "\n");
+                if (o.first == name) {
+                    for(const auto &contents : o.second) {
+                        fprintf(out, "%s", contents.c_str());
+                        fprintf(out, "\n");
+                    }
+                }
+            }
+
+            fprintf(out, "#endif\n");
+            fprintf(out, "\n");
+        }
+        if (out != stdout) { fclose(out); }
+        return 0;
+    }
+};
+
+static c_sdk_output::factory creator;
diff --git a/tools/pioasm/gen/lexer.cpp b/tools/pioasm/gen/lexer.cpp
new file mode 100644
index 0000000..5de6626
--- /dev/null
+++ b/tools/pioasm/gen/lexer.cpp
@@ -0,0 +1,2697 @@
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+/* %not-for-header */
+/* %if-c-only */
+/* %if-not-reentrant */
+
+/* %endif */
+/* %endif */
+/* %ok-for-header */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* %if-c++-only */
+/* %endif */
+
+/* %if-c-only */
+
+/* %endif */
+
+/* %if-c-only */
+
+/* %endif */
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+/* %if-c-only */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+/* %endif */
+
+/* %if-tables-serialization */
+/* %endif */
+/* end standard C headers. */
+
+/* begin standard C++ headers. */
+/* %if-c++-only */
+/* %endif */
+
+/* %if-c-or-c++ */
+/* flex integer type definitions */
+
+#ifndef YYFLEX_INTTYPES_DEFINED
+#define YYFLEX_INTTYPES_DEFINED
+
+/* Prefer C99 integer types if available. */
+# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+/* Include <inttypes.h> and not <stdint.h> because Solaris 2.6 has the former
+ * and not the latter.
+ */
+#include <inttypes.h>
+#  define YYFLEX_USE_STDINT
+# else
+#  if defined(_MSC_VER) && _MSC_VER >= 1600
+/* Visual C++ 2010 does not define __STDC_VERSION__ and has <stdint.h> but not
+ * <inttypes.h>.
+ */
+#include <stdint.h>
+#   define YYFLEX_USE_STDINT
+#  endif
+# endif
+# ifdef YYFLEX_USE_STDINT
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+# else
+typedef unsigned char flex_uint8_t;
+typedef short int flex_int16_t;
+typedef unsigned short int flex_uint16_t;
+#  ifdef __STDC__
+typedef signed char flex_int8_t;
+/* ISO C only requires at least 16 bits for int. */
+#include <limits.h>
+#   if UINT_MAX >= 4294967295
+#    define YYFLEX_INT32_DEFINED
+typedef int flex_int32_t;
+typedef unsigned int flex_uint32_t;
+#   endif
+#  else
+typedef char flex_int8_t;
+#  endif
+#  ifndef YYFLEX_INT32_DEFINED
+typedef long int flex_int32_t;
+typedef unsigned long int flex_uint32_t;
+#  endif
+# endif
+#endif /* YYFLEX_INTTYPES_DEFINED */
+
+/* %endif */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* %not-for-header */
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+/* %ok-for-header */
+
+/* %not-for-header */
+/* Promotes a possibly negative, possibly signed char to an
+ *   integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+/* %ok-for-header */
+
+/* %if-reentrant */
+/* %endif */
+
+/* %if-not-reentrant */
+
+/* %endif */
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin  )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+/* %if-not-reentrant */
+extern int yyleng;
+/* %endif */
+
+/* %if-c-only */
+/* %if-not-reentrant */
+extern FILE *yyin, *yyout;
+/* %endif */
+/* %endif */
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+    
+    #define YY_LESS_LINENO(n)
+    #define YY_LINENO_REWIND_TO(ptr)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+/* %if-c-only */
+	FILE *yy_input_file;
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	int yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+/* %if-not-reentrant */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */
+/* %endif */
+/* %ok-for-header */
+
+/* %endif */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* %if-c-only Standard (non-C++) definition */
+
+/* %if-not-reentrant */
+/* %not-for-header */
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = NULL;
+static int yy_init = 0;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+/* %ok-for-header */
+
+/* %endif */
+
+void yyrestart ( FILE *input_file  );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size  );
+void yy_delete_buffer ( YY_BUFFER_STATE b  );
+void yy_flush_buffer ( YY_BUFFER_STATE b  );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state ( void );
+
+static void yyensure_buffer_stack ( void );
+static void yy_load_buffer_state ( void );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file  );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len  );
+
+/* %endif */
+
+void *yyalloc ( yy_size_t  );
+void *yyrealloc ( void *, yy_size_t  );
+void yyfree ( void *  );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
+/* Begin user sect3 */
+
+#define yywrap() (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define FLEX_DEBUG
+typedef flex_uint8_t YY_CHAR;
+
+FILE *yyin = NULL, *yyout = NULL;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+int yylineno = 1;
+
+extern char *yytext;
+#ifdef yytext_ptr
+#undef yytext_ptr
+#endif
+#define yytext_ptr yytext
+
+/* %% [1.5] DFA */
+
+/* %if-c-only Standard (non-C++) definition */
+
+static yy_state_type yy_get_previous_state ( void );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state  );
+static int yy_get_next_buffer ( void );
+static void yynoreturn yy_fatal_error ( const char* msg  );
+
+/* %endif */
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
+	yyleng = (int) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
+	(yy_c_buf_p) = yy_cp;
+/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
+#define YY_NUM_RULES 95
+#define YY_END_OF_BUFFER 96
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static const flex_int16_t yy_accept[263] =
+    {   0,
+        0,    0,    7,    7,   11,   11,    0,    0,   96,   94,
+        1,    2,   40,   94,   37,   28,   29,   34,   30,   23,
+       33,   94,   35,   89,   89,   25,   93,   92,   92,   92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,   92,
+       92,   73,   74,   92,   26,   27,   38,   36,   41,   94,
+        7,    4,    5,    7,   11,    8,   12,   10,   19,   14,
+       20,   21,   16,   16,   15,    1,    2,   39,    0,    0,
+       31,   50,   50,   50,   50,   50,   50,   50,   22,   93,
+       89,    0,    0,   24,   93,   92,   92,   92,   92,   92,
+       92,   53,   92,   92,   92,   92,   92,   92,   92,   92,
+
+       92,   92,   75,   92,   92,   92,   92,   92,   92,   92,
+       92,    0,    7,    4,    5,    6,   11,    8,   12,    9,
+       19,   14,   20,    0,   13,   16,   19,   19,    0,    3,
+        0,   50,   50,   50,   50,   50,   50,   50,   50,   91,
+       90,   92,   92,   92,   92,   92,   92,   58,   77,   51,
+       57,   92,   60,   92,   92,   87,   63,   78,   54,   67,
+       92,   92,   92,   84,   59,   92,   92,   92,   92,    0,
+       18,   17,   50,   50,   50,   50,   50,   50,   50,   92,
+       92,   76,   68,   92,   92,   92,   92,   71,   92,   69,
+       92,   70,   92,   56,   55,   64,   92,   52,   88,    0,
+
+       50,   50,   50,   50,   50,   45,   44,   80,   85,   92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,    0,
+       50,   50,   50,   50,   50,   50,   92,   82,   92,   86,
+       92,   92,   61,   92,   92,   79,   32,   46,   50,   48,
+       50,   50,   50,   83,   81,   92,   72,   65,   92,   50,
+       42,   50,   50,   62,   66,   49,   47,   50,   50,   50,
+       43,    0
+    } ;
+
+static const YY_CHAR yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    1,    1,    6,    7,    1,    8,
+        9,   10,   11,   12,   13,   14,   15,   16,   17,   18,
+       18,   18,   18,   18,   18,   18,   18,   19,   20,    1,
+       21,    1,    1,    1,   22,   23,   24,   25,   26,   27,
+       28,   29,   30,   31,   32,   33,   34,   35,   36,   37,
+       38,   39,   40,   41,   42,   43,   44,   45,   46,   47,
+       48,    1,   49,   50,   51,    1,   52,   53,   54,   55,
+
+       56,   57,   58,   59,   60,   61,   62,   63,   64,   65,
+       66,   67,   68,   69,   70,   71,   72,   73,   74,   75,
+       76,   77,   78,   79,   80,   81,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,   82,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,   83,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,   84,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static const YY_CHAR yy_meta[85] =
+    {   0,
+        1,    2,    3,    1,    2,    4,    1,    1,    1,    5,
+        1,    1,    1,    1,    1,    6,    6,    6,    1,    1,
+        2,    7,    7,    7,    7,    7,    7,    8,    8,    8,
+        8,    8,    8,    8,    8,    8,    8,    8,    8,    8,
+        8,    8,    8,    8,    8,    8,    8,    1,    1,    1,
+        8,    7,    7,    7,    7,    7,    7,    8,    8,    8,
+        8,    8,    8,    8,    8,    8,    8,    8,    8,    8,
+        8,    8,    8,    8,    8,    8,    8,    1,    1,    1,
+        1,    1,    1,    1
+    } ;
+
+static const flex_int16_t yy_base[276] =
+    {   0,
+        0,    0,   83,   85,   90,   92,   94,  101,  516,  605,
+      513,  511,  492,  510,  605,  605,  605,  605,  605,  605,
+      498,   88,   99,  118,  113,  490,    0,    0,   57,   74,
+       63,   79,  113,   92,   91,  103,  107,  143,  124,  135,
+      137,    0,    0,  134,  605,  605,  605,  605,  605,  420,
+        0,  499,  497,  419,    0,  496,  494,  481,    0,  493,
+      491,  488,  182,  170,  605,  399,  364,  605,  103,  131,
+      605,    0,  138,  144,  145,  153,  171,  168,  605,    0,
+      194,  200,    0,  605,    0,    0,  166,  182,  187,  188,
+      193,    0,  183,  184,  187,  182,  203,  195,  203,  189,
+
+      194,  195,    0,  204,  238,  209,  203,  220,  224,  222,
+      223,  283,    0,  363,  361,    0,    0,  360,  355,  605,
+        0,  355,  353,  308,  285,  263,  248,  293,  265,  266,
+      281,    0,  246,  249,  263,  258,  270,  257,  275,  269,
+        0,  274,  277,  276,  266,  270,  263,    0,    0,    0,
+        0,  273,    0,  285,  279,    0,  291,  296,    0,  313,
+      298,  300,  310,    0,    0,  314,  300,  313,  319,  205,
+      271,  370,  330,  344,  345,  346,  349,  351,  340,  346,
+      340,    0,    0,  343,  348,  346,  359,    0,  355,    0,
+      368,    0,  369,    0,    0,  380,  358,    0,    0,  159,
+
+      378,  117,  384,  376,   72,    0,   50,    0,    0,  375,
+      384,  394,  391,  398,  395,  411,  410,  397,  398,   15,
+      413,  404,  406,  420,  409,  410,  406,    0,  421,    0,
+      432,  415,    0,  415,  431,    0,  605,    0,  421,    0,
+      425,  434,  439,    0,    0,  440,    0,    0,  433,  434,
+        0,  435,  438,    0,    0,    0,    0,  450,  454,  451,
+        0,  605,  522,  530,  538,  546,  548,  556,  559,  567,
+      575,  583,  591,  594,  597
+    } ;
+
+static const flex_int16_t yy_def[276] =
+    {   0,
+      262,    1,  263,  263,  264,  264,  265,  265,  262,  262,
+      262,  262,  262,  266,  262,  262,  262,  262,  262,  262,
+      262,  267,  262,  262,  262,  262,  268,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+      269,  269,  269,  269,  262,  262,  262,  262,  262,  262,
+      270,  270,  262,  270,  271,  271,  262,  262,  272,  262,
+      262,  273,  272,  272,  262,  262,  262,  262,  266,  266,
+      262,  274,  274,  274,  274,  274,  274,  274,  262,  268,
+      262,  262,  275,  262,  268,  269,  269,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+      269,  262,  270,  270,  262,  270,  271,  271,  262,  262,
+      272,  262,  262,  273,  273,  272,  272,  272,  266,  266,
+      266,  274,  274,  274,  274,  274,  274,  274,  274,  262,
+      275,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  262,
+      272,  272,  274,  274,  274,  274,  274,  274,  274,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  262,
+
+      274,  274,  274,  274,  274,  274,  274,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  269,  269,  269,  262,
+      274,  274,  274,  274,  274,  274,  269,  269,  269,  269,
+      269,  269,  269,  269,  269,  269,  262,  274,  274,  274,
+      274,  274,  274,  269,  269,  269,  269,  269,  269,  274,
+      274,  274,  274,  269,  269,  274,  274,  274,  274,  274,
+      274,    0,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262
+    } ;
+
+static const flex_int16_t yy_nxt[690] =
+    {   0,
+       10,   11,   12,   13,   10,   14,   15,   16,   17,   18,
+       19,   20,   21,   22,   23,   24,   25,   25,   26,   27,
+       10,   28,   29,   30,   28,   31,   28,   32,   28,   33,
+       34,   28,   28,   35,   36,   37,   38,   28,   39,   40,
+       28,   28,   28,   41,   42,   43,   44,   45,   46,   47,
+       28,   28,   29,   30,   28,   31,   28,   32,   28,   33,
+       34,   28,   28,   35,   36,   37,   38,   28,   39,   40,
+       28,   28,   28,   41,   42,   43,   44,   10,   48,   10,
+       49,   10,   10,   50,   52,   53,   52,   53,   54,   87,
+       54,   56,   57,   56,   57,   60,   61,  237,   62,   58,
+
+      226,   58,   60,   61,  129,   62,   88,   89,   79,   63,
+       64,   64,   73,   80,   65,   90,   63,   64,   64,   87,
+       74,   65,  225,   75,   76,   95,   96,   77,   81,   81,
+       81,   78,  131,   81,   81,   81,   88,   89,   97,   91,
+       82,   99,   73,  100,   98,   90,  101,   92,  102,  106,
+       74,   93,   94,   75,   76,   95,   96,   77,  110,  111,
+      107,   78,   83,  133,  108,  134,  103,  222,   97,   91,
+       82,   99,  104,  100,   98,  109,  101,   92,  102,  106,
+      130,   93,   94,  135,  105,  126,  126,  126,  110,  111,
+      107,  136,   83,  133,  108,  134,  103,  126,  126,  126,
+
+      137,  142,  104,  138,  127,  109,  139,  143,  130,   81,
+       81,   81,  144,  135,  105,  140,  140,  145,  146,  147,
+      148,  136,  149,  150,  151,  152,  128,  155,  156,  157,
+      137,  142,  158,  138,  127,  159,  139,  143,  160,  153,
+      220,  164,  144,  165,  166,  167,  154,  145,  146,  147,
+      148,  168,  149,  150,  151,  152,  128,  155,  156,  157,
+      161,  169,  158,  171,  171,  159,  129,  129,  160,  153,
+      162,  164,  173,  165,  166,  167,  154,  163,  126,  126,
+      126,  168,  131,  174,  140,  140,  171,  171,  200,  125,
+      161,  169,  175,  176,  177,  178,  179,  180,  181,  182,
+
+      162,  183,  173,  184,  185,  186,  187,  163,  172,  172,
+      172,  188,  125,  174,  172,  172,  172,  172,  172,  172,
+      189,  190,  175,  176,  177,  178,  179,  180,  181,  182,
+      193,  183,  194,  184,  185,  186,  187,  191,  195,  196,
+      197,  188,  130,  130,  172,  172,  172,  172,  172,  172,
+      189,  190,  192,  198,  199,  123,  122,  119,  130,  201,
+      193,  118,  194,  115,  114,  170,   67,  191,  195,  196,
+      197,  202,  203,  204,  205,  206,  207,  208,  209,  210,
+      211,  212,  192,  198,  199,  172,  172,  172,  213,  201,
+      214,  172,  172,  172,  172,  172,  172,  215,  216,  219,
+
+       66,  202,  203,  204,  205,  206,  207,  208,  209,  210,
+      211,  212,  221,  223,  224,  227,  228,  229,  213,  217,
+      214,  172,  172,  172,  172,  172,  172,  215,  216,  219,
+      218,  230,  231,  232,  233,  234,  235,  236,  238,  239,
+      240,  241,  221,  223,  224,  227,  228,  229,  242,  217,
+      243,  244,  245,  246,  247,  248,  249,  250,  251,  252,
+      253,  230,  231,  232,  233,  234,  235,  236,  238,  239,
+      240,  241,  254,  255,  256,  257,  258,  259,  242,  260,
+      243,  244,  245,  246,  247,  248,  249,  250,  251,  252,
+      253,  261,  125,  123,  122,  120,  119,  118,  116,  115,
+
+      114,  112,  254,  255,  256,  257,  258,  259,   84,  260,
+       71,   70,   68,   67,   66,  262,  262,  262,  262,  262,
+      262,  261,   51,   51,   51,   51,   51,   51,   51,   51,
+       55,   55,   55,   55,   55,   55,   55,   55,   59,   59,
+       59,   59,   59,   59,   59,   59,   69,   69,  262,  262,
+       69,   69,   69,   69,   72,   72,   85,   85,  262,   85,
+       85,   85,   85,   85,   86,   86,   86,  113,  113,  262,
+      113,  113,  113,  113,  113,  117,  117,  262,  117,  262,
+      117,  117,  117,  121,  262,  262,  121,  121,  121,  121,
+      121,  124,  124,  262,  124,  124,  124,  124,  124,  132,
+
+      132,  132,  141,  141,    9,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262
+    } ;
+
+static const flex_int16_t yy_chk[690] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    3,    3,    4,    4,    3,   29,
+        4,    5,    5,    6,    6,    7,    7,  220,    7,    5,
+
+      207,    6,    8,    8,   69,    8,   30,   31,   23,    7,
+        7,    7,   22,   23,    7,   32,    8,    8,    8,   29,
+       22,    8,  205,   22,   22,   34,   35,   22,   25,   25,
+       25,   22,   70,   24,   24,   24,   30,   31,   36,   33,
+       24,   37,   22,   37,   36,   32,   37,   33,   37,   39,
+       22,   33,   33,   22,   22,   34,   35,   22,   41,   44,
+       40,   22,   24,   73,   40,   74,   38,  202,   36,   33,
+       24,   37,   38,   37,   36,   40,   37,   33,   37,   39,
+       69,   33,   33,   75,   38,   64,   64,   64,   41,   44,
+       40,   76,   24,   73,   40,   74,   38,   63,   63,   63,
+
+       77,   87,   38,   78,   63,   40,   78,   88,   70,   81,
+       81,   81,   89,   75,   38,   82,   82,   90,   91,   91,
+       93,   76,   94,   95,   96,   97,   63,   98,   99,  100,
+       77,   87,  101,   78,   63,  102,   78,   88,  104,   97,
+      200,  106,   89,  107,  108,  109,   97,   90,   91,   91,
+       93,  110,   94,   95,   96,   97,   63,   98,   99,  100,
+      105,  111,  101,  127,  127,  102,  129,  130,  104,   97,
+      105,  106,  133,  107,  108,  109,   97,  105,  126,  126,
+      126,  110,  131,  134,  140,  140,  171,  171,  170,  125,
+      105,  111,  135,  136,  137,  138,  139,  142,  143,  144,
+
+      105,  145,  133,  146,  147,  152,  154,  105,  128,  128,
+      128,  155,  124,  134,  128,  128,  128,  128,  128,  128,
+      157,  158,  135,  136,  137,  138,  139,  142,  143,  144,
+      161,  145,  162,  146,  147,  152,  154,  160,  163,  166,
+      167,  155,  129,  130,  128,  128,  128,  128,  128,  128,
+      157,  158,  160,  168,  169,  123,  122,  119,  131,  173,
+      161,  118,  162,  115,  114,  112,   67,  160,  163,  166,
+      167,  174,  175,  176,  177,  178,  179,  180,  181,  184,
+      185,  186,  160,  168,  169,  172,  172,  172,  187,  173,
+      189,  172,  172,  172,  172,  172,  172,  191,  193,  197,
+
+       66,  174,  175,  176,  177,  178,  179,  180,  181,  184,
+      185,  186,  201,  203,  204,  210,  211,  212,  187,  196,
+      189,  172,  172,  172,  172,  172,  172,  191,  193,  197,
+      196,  213,  214,  215,  216,  217,  218,  219,  221,  222,
+      223,  224,  201,  203,  204,  210,  211,  212,  225,  196,
+      226,  227,  229,  231,  232,  234,  235,  239,  241,  242,
+      243,  213,  214,  215,  216,  217,  218,  219,  221,  222,
+      223,  224,  246,  249,  250,  252,  253,  258,  225,  259,
+      226,  227,  229,  231,  232,  234,  235,  239,  241,  242,
+      243,  260,   62,   61,   60,   58,   57,   56,   54,   53,
+
+       52,   50,  246,  249,  250,  252,  253,  258,   26,  259,
+       21,   14,   13,   12,   11,    9,    0,    0,    0,    0,
+        0,  260,  263,  263,  263,  263,  263,  263,  263,  263,
+      264,  264,  264,  264,  264,  264,  264,  264,  265,  265,
+      265,  265,  265,  265,  265,  265,  266,  266,    0,    0,
+      266,  266,  266,  266,  267,  267,  268,  268,    0,  268,
+      268,  268,  268,  268,  269,  269,  269,  270,  270,    0,
+      270,  270,  270,  270,  270,  271,  271,    0,  271,    0,
+      271,  271,  271,  272,    0,    0,  272,  272,  272,  272,
+      272,  273,  273,    0,  273,  273,  273,  273,  273,  274,
+
+      274,  274,  275,  275,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262,  262,
+      262,  262,  262,  262,  262,  262,  262,  262,  262
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 1;
+
+static const flex_int16_t yy_rule_linenum[95] =
+    {   0,
+       65,   66,   68,   79,   80,   81,   82,   86,   87,   88,
+       89,   90,   94,   95,   96,   97,   98,   99,  100,  101,
+      102,  105,  106,  107,  108,  109,  110,  111,  112,  113,
+      114,  115,  116,  117,  118,  119,  120,  121,  122,  123,
+      124,  126,  127,  128,  129,  130,  131,  132,  133,  134,
+      136,  137,  138,  139,  140,  141,  142,  143,  144,  145,
+      147,  149,  150,  151,  152,  153,  154,  155,  156,  158,
+      159,  160,  161,  162,  163,  164,  165,  166,  167,  169,
+      170,  171,  172,  173,  175,  176,  178,  179,  183,  184,
+      185,  187,  189,  191
+
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lexer.ll"
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#line 8 "lexer.ll"
+# include <cerrno>
+# include <climits>
+# include <cstdlib>
+# include <cstring>
+# include <string>
+# include "pio_assembler.h"
+# include "parser.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4996) // fopen
+#endif
+
+#define YY_NO_INPUT 1
+  yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc);
+  yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc);
+  yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc);
+  // Code run each time a pattern is matched.
+  # define YY_USER_ACTION  loc.columns (yyleng);
+
+#define INITIAL 0
+#define code_block 1
+#define c_comment 2
+#define lang_opt 3
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+/* %if-c-only */
+#include <unistd.h>
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* %if-c-only Reentrant structure and macros (non-C++). */
+/* %if-reentrant */
+/* %if-c-only */
+
+static int yy_init_globals ( void );
+
+/* %endif */
+/* %if-reentrant */
+/* %endif */
+/* %endif End reentrant structures and macros. */
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( void );
+
+int yyget_debug ( void );
+
+void yyset_debug ( int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra ( void );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in ( void );
+
+void yyset_in  ( FILE * _in_str  );
+
+FILE *yyget_out ( void );
+
+void yyset_out  ( FILE * _out_str  );
+
+			int yyget_leng ( void );
+
+char *yyget_text ( void );
+
+int yyget_lineno ( void );
+
+void yyset_lineno ( int _line_number  );
+
+/* %if-bison-bridge */
+/* %endif */
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( void );
+#else
+extern int yywrap ( void );
+#endif
+#endif
+
+/* %not-for-header */
+#ifndef YY_NO_UNPUT
+    
+#endif
+/* %ok-for-header */
+
+/* %endif */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * );
+#endif
+
+#ifndef YY_NO_INPUT
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+#ifdef __cplusplus
+static int yyinput ( void );
+#else
+static int input ( void );
+#endif
+/* %ok-for-header */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+
+/* %endif */
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* %if-c-only Standard (non-C++) definition */
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+/* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		int n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+/* %if-c++-only C++ definition \ */\
+/* %endif */
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+/* %if-c-only */
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+/* %if-tables-serialization structures and prototypes */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %not-for-header */
+/* %tables-yydmap generated elements */
+/* %endif */
+/* end tables serialization structures and prototypes */
+
+/* %ok-for-header */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+/* %if-c-only Standard (non-C++) definition */
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+/* %% [6.0] YY_RULE_SETUP definition goes here */
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/* %not-for-header */
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
+    
+	if ( !(yy_init) )
+		{
+		(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! yyin )
+/* %if-c-only */
+			yyin = stdin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+		if ( ! yyout )
+/* %if-c-only */
+			yyout = stdout;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+		}
+
+		yy_load_buffer_state(  );
+		}
+
+	{
+/* %% [7.0] user's declarations go here */
+
+        std::string code_block_contents;
+        yy::location code_block_start;
+
+  // A handy shortcut to the location held by the pio_assembler.
+  yy::location& loc = pioasm.location;
+  // Code run each time yylex is called.
+  loc.step();
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
+		{
+/* %% [8.0] yymore()-related code goes here */
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of yytext. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+/* %% [9.0] code to set up and find next match goes here */
+		yy_current_state = (yy_start);
+yy_match:
+		do
+			{
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			if ( yy_accept[yy_current_state] )
+				{
+				(yy_last_accepting_state) = yy_current_state;
+				(yy_last_accepting_cpos) = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 263 )
+					yy_c = yy_meta[yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+			++yy_cp;
+			}
+		while ( yy_current_state != 262 );
+		yy_cp = (yy_last_accepting_cpos);
+		yy_current_state = (yy_last_accepting_state);
+
+yy_find_action:
+/* %% [10.0] code to find the action number goes here */
+		yy_act = yy_accept[yy_current_state];
+
+		YY_DO_BEFORE_ACTION;
+
+/* %% [11.0] code for yylineno update goes here */
+
+do_action:	/* This label is used only to access EOF actions. */
+
+/* %% [12.0] debug code goes here */
+		if ( yy_flex_debug )
+			{
+			if ( yy_act == 0 )
+				fprintf( stderr, "--scanner backing up\n" );
+			else if ( yy_act < 95 )
+				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
+				         (long)yy_rule_linenum[yy_act], yytext );
+			else if ( yy_act == 95 )
+				fprintf( stderr, "--accepting default rule (\"%s\")\n",
+				         yytext );
+			else if ( yy_act == 96 )
+				fprintf( stderr, "--(end of buffer or a NUL)\n" );
+			else
+				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
+			}
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+/* %% [13.0] actions go here */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = (yy_hold_char);
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+loc.step();
+	YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{ auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline); }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+{
+                                        BEGIN(code_block);
+                                        code_block_contents = "";
+                                        code_block_start = loc;
+                                        std::string tmp(yytext);
+                                        tmp = tmp.substr(1, tmp.length() - 2);
+                                        tmp = tmp.erase(0, tmp.find_first_not_of(" \t"));
+                                        tmp = tmp.erase(tmp.find_last_not_of(" \t") + 1);
+                                        return yy::parser::make_CODE_BLOCK_START( tmp, loc);
+                                    }
+	YY_BREAK
+
+case 4:
+YY_RULE_SETUP
+loc.step();
+	YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+{ auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+{ BEGIN(INITIAL); auto loc2 = loc; loc2.begin = code_block_start.begin; return yy::parser::make_CODE_BLOCK_CONTENTS(code_block_contents, loc2); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+{ code_block_contents += std::string(yytext) + "\n"; }
+	YY_BREAK
+
+case 8:
+YY_RULE_SETUP
+loc.step();
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+{ BEGIN(INITIAL); }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+{ }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+{ }
+	YY_BREAK
+case 12:
+/* rule 12 can match eol */
+YY_RULE_SETUP
+{ auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
+	YY_BREAK
+
+case 13:
+YY_RULE_SETUP
+return yy::parser::make_STRING(yytext, loc);
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+loc.step();
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+return yy::parser::make_EQUAL(loc);
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+return make_INT(yytext, loc);
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+return make_HEX(yytext, loc);
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+return make_BINARY(yytext, loc);
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+return yy::parser::make_NON_WS(yytext, loc);
+	YY_BREAK
+case 20:
+/* rule 20 can match eol */
+YY_RULE_SETUP
+{ BEGIN(INITIAL); auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline);  }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+{ throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
+	YY_BREAK
+
+case 22:
+YY_RULE_SETUP
+{ BEGIN(c_comment); }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+return yy::parser::make_COMMA(loc);
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+return yy::parser::make_REVERSE(loc);
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+return yy::parser::make_COLON(loc);
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+return yy::parser::make_LBRACKET(loc);
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+return yy::parser::make_RBRACKET(loc);
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+return yy::parser::make_LPAREN(loc);
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+return yy::parser::make_RPAREN(loc);
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+return yy::parser::make_PLUS(loc);
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+return yy::parser::make_POST_DECREMENT(loc);
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+return yy::parser::make_POST_DECREMENT(loc);
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+return yy::parser::make_MINUS(loc);
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+return yy::parser::make_MULTIPLY(loc);
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+return yy::parser::make_DIVIDE(loc);
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+return yy::parser::make_OR(loc);
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+return yy::parser::make_AND(loc);
+	YY_BREAK
+case 38:
+YY_RULE_SETUP
+return yy::parser::make_XOR(loc);
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+return yy::parser::make_NOT_EQUAL(loc);
+	YY_BREAK
+case 40:
+YY_RULE_SETUP
+return yy::parser::make_NOT(loc);
+	YY_BREAK
+case 41:
+YY_RULE_SETUP
+return yy::parser::make_NOT(loc);
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+return yy::parser::make_PROGRAM(loc);
+	YY_BREAK
+case 43:
+YY_RULE_SETUP
+return yy::parser::make_WRAP_TARGET(loc);
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+return yy::parser::make_WRAP(loc);
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+return yy::parser::make_WORD(loc);
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+return yy::parser::make_DEFINE(loc);
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+return yy::parser::make_SIDE_SET(loc);
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+return yy::parser::make_ORIGIN(loc);
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+{ BEGIN(lang_opt); return yy::parser::make_LANG_OPT(loc); }
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+return yy::parser::make_UNKNOWN_DIRECTIVE(yytext, loc);
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+return yy::parser::make_JMP(loc);
+	YY_BREAK
+case 52:
+YY_RULE_SETUP
+return yy::parser::make_WAIT(loc);
+	YY_BREAK
+case 53:
+YY_RULE_SETUP
+return yy::parser::make_IN(loc);
+	YY_BREAK
+case 54:
+YY_RULE_SETUP
+return yy::parser::make_OUT(loc);
+	YY_BREAK
+case 55:
+YY_RULE_SETUP
+return yy::parser::make_PUSH(loc);
+	YY_BREAK
+case 56:
+YY_RULE_SETUP
+return yy::parser::make_PULL(loc);
+	YY_BREAK
+case 57:
+YY_RULE_SETUP
+return yy::parser::make_MOV(loc);
+	YY_BREAK
+case 58:
+YY_RULE_SETUP
+return yy::parser::make_IRQ(loc);
+	YY_BREAK
+case 59:
+YY_RULE_SETUP
+return yy::parser::make_SET(loc);
+	YY_BREAK
+case 60:
+YY_RULE_SETUP
+return yy::parser::make_NOP(loc);
+	YY_BREAK
+case 61:
+YY_RULE_SETUP
+return yy::parser::make_PUBLIC(loc);
+	YY_BREAK
+case 62:
+YY_RULE_SETUP
+return yy::parser::make_OPTIONAL(loc);
+	YY_BREAK
+case 63:
+YY_RULE_SETUP
+return yy::parser::make_OPTIONAL(loc);
+	YY_BREAK
+case 64:
+YY_RULE_SETUP
+return yy::parser::make_SIDE(loc);
+	YY_BREAK
+case 65:
+YY_RULE_SETUP
+return yy::parser::make_SIDE(loc);
+	YY_BREAK
+case 66:
+YY_RULE_SETUP
+return yy::parser::make_SIDE(loc);
+	YY_BREAK
+case 67:
+YY_RULE_SETUP
+return yy::parser::make_PIN(loc);
+	YY_BREAK
+case 68:
+YY_RULE_SETUP
+return yy::parser::make_GPIO(loc);
+	YY_BREAK
+case 69:
+YY_RULE_SETUP
+return yy::parser::make_OSRE(loc);
+	YY_BREAK
+case 70:
+YY_RULE_SETUP
+return yy::parser::make_PINS(loc);
+	YY_BREAK
+case 71:
+YY_RULE_SETUP
+return yy::parser::make_NULL(loc);
+	YY_BREAK
+case 72:
+YY_RULE_SETUP
+return yy::parser::make_PINDIRS(loc);
+	YY_BREAK
+case 73:
+YY_RULE_SETUP
+return yy::parser::make_X(loc);
+	YY_BREAK
+case 74:
+YY_RULE_SETUP
+return yy::parser::make_Y(loc);
+	YY_BREAK
+case 75:
+YY_RULE_SETUP
+return yy::parser::make_PC(loc);
+	YY_BREAK
+case 76:
+YY_RULE_SETUP
+return yy::parser::make_EXEC(loc);
+	YY_BREAK
+case 77:
+YY_RULE_SETUP
+return yy::parser::make_ISR(loc);
+	YY_BREAK
+case 78:
+YY_RULE_SETUP
+return yy::parser::make_OSR(loc);
+	YY_BREAK
+case 79:
+YY_RULE_SETUP
+return yy::parser::make_STATUS(loc);
+	YY_BREAK
+case 80:
+YY_RULE_SETUP
+return yy::parser::make_BLOCK(loc);
+	YY_BREAK
+case 81:
+YY_RULE_SETUP
+return yy::parser::make_NOBLOCK(loc);
+	YY_BREAK
+case 82:
+YY_RULE_SETUP
+return yy::parser::make_IFFULL(loc);
+	YY_BREAK
+case 83:
+YY_RULE_SETUP
+return yy::parser::make_IFEMPTY(loc);
+	YY_BREAK
+case 84:
+YY_RULE_SETUP
+return yy::parser::make_REL(loc);
+	YY_BREAK
+case 85:
+YY_RULE_SETUP
+return yy::parser::make_CLEAR(loc);
+	YY_BREAK
+case 86:
+YY_RULE_SETUP
+return yy::parser::make_NOWAIT(loc);
+	YY_BREAK
+case 87:
+YY_RULE_SETUP
+return yy::parser::make_INT(1, loc);
+	YY_BREAK
+case 88:
+YY_RULE_SETUP
+return yy::parser::make_INT(0, loc);
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(code_block):
+case YY_STATE_EOF(c_comment):
+case YY_STATE_EOF(lang_opt):
+return yy::parser::make_END(loc);
+	YY_BREAK
+case 89:
+YY_RULE_SETUP
+return make_INT(yytext, loc);
+	YY_BREAK
+case 90:
+YY_RULE_SETUP
+return make_HEX(yytext, loc);
+	YY_BREAK
+case 91:
+YY_RULE_SETUP
+return make_BINARY(yytext, loc);
+	YY_BREAK
+case 92:
+YY_RULE_SETUP
+return yy::parser::make_ID(yytext, loc);
+	YY_BREAK
+case 93:
+YY_RULE_SETUP
+{ }
+	YY_BREAK
+case 94:
+YY_RULE_SETUP
+{ throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
+	YY_BREAK
+case 95:
+YY_RULE_SETUP
+ECHO;
+	YY_BREAK
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+/* %if-c-only */
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
+				yy_cp = (yy_last_accepting_cpos);
+				yy_current_state = (yy_last_accepting_state);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( yywrap(  ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of user's declarations */
+} /* end of yylex */
+/* %ok-for-header */
+
+/* %if-c++-only */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %endif */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+/* %if-c-only */
+static int yy_get_next_buffer (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = (yytext_ptr);
+	int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1);
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc( (void *) b->yy_ch_buf,
+							 (yy_size_t) (b->yy_buf_size + 2)  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = NULL;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+			(void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size  );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+		/* "- 2" to take care of EOB's */
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+	}
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+/* %if-c-only */
+/* %not-for-header */
+    static yy_state_type yy_get_previous_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+	yy_state_type yy_current_state;
+	char *yy_cp;
+    
+/* %% [15.0] code to get the start state into yy_current_state goes here */
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+/* %% [16.0] code to find the next state goes here */
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			(yy_last_accepting_state) = yy_current_state;
+			(yy_last_accepting_cpos) = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 263 )
+				yy_c = yy_meta[yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+/* %if-c-only */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+	int yy_is_jam;
+    /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
+	char *yy_cp = (yy_c_buf_p);
+
+	YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		(yy_last_accepting_state) = yy_current_state;
+		(yy_last_accepting_cpos) = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 263 )
+			yy_c = yy_meta[yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+	yy_is_jam = (yy_current_state == 262);
+
+		return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+/* %if-c-only */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (int) ((yy_c_buf_p) - (yytext_ptr));
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap(  ) )
+						return 0;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve yytext */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+/* %% [19.0] update BOL and yylineno */
+
+	return c;
+}
+/* %if-c-only */
+#endif	/* ifndef YY_NO_INPUT */
+/* %endif */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+/* %if-c-only */
+    void yyrestart  (FILE * input_file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer( yyin, YY_BUF_SIZE );
+	}
+
+	yy_init_buffer( YY_CURRENT_BUFFER, input_file );
+	yy_load_buffer_state(  );
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+/* %if-c-only */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state(  );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/* %if-c-only */
+static void yy_load_buffer_state  (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+/* %if-c-only */
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+/* %if-c-only */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2)  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+/* %if-c-only */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree( (void *) b->yy_ch_buf  );
+
+	yyfree( (void *) b  );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+/* %if-c-only */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+{
+	int oerrno = errno;
+    
+	yy_flush_buffer( b );
+
+/* %if-c-only */
+	b->yy_input_file = file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+/* %if-c-only */
+
+        b->yy_is_interactive = 0;
+    
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+/* %if-c-only */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state(  );
+}
+
+/* %if-c-or-c++ */
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+/* %if-c-only */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack();
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state(  );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+/* %if-c-only */
+void yypop_buffer_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state(  );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+/* %if-c-only */
+static void yyensure_buffer_stack (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+	yy_size_t num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return NULL;
+
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = (int) (size - 2);	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = NULL;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b  );
+
+	return b;
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr )
+{
+    
+	return yy_scan_bytes( yystr, (int) strlen(yystr) );
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = (yy_size_t) (_yybytes_len + 2);
+	buf = (char *) yyalloc( n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+/* %endif */
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+/* %if-c-only */
+static void yynoreturn yy_fatal_error (const char* msg )
+{
+			fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = (yy_hold_char); \
+		(yy_c_buf_p) = yytext + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/* %if-c-only */
+/* %if-reentrant */
+/* %endif */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+    
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/* %if-reentrant */
+/* %endif */
+
+/** Set the current line number.
+ * @param _line_number line number
+ * 
+ */
+void yyset_lineno (int  _line_number )
+{
+    
+    yylineno = _line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  _in_str )
+{
+        yyin = _in_str ;
+}
+
+void yyset_out (FILE *  _out_str )
+{
+        yyout = _out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  _bdebug )
+{
+        yy_flex_debug = _bdebug ;
+}
+
+/* %endif */
+
+/* %if-reentrant */
+/* %if-bison-bridge */
+/* %endif */
+/* %endif if-c-only */
+
+/* %if-c-only */
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = NULL;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = NULL;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = NULL;
+    yyout = NULL;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+/* %endif */
+
+/* %if-c-only SNIP! this currently causes conflicts with the c++ scanner */
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer( YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	yyfree((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+/* %if-reentrant */
+/* %endif */
+    return 0;
+}
+/* %endif */
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n )
+{
+		
+	int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s )
+{
+	int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+			return malloc(size);
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+		
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return realloc(ptr, size);
+}
+
+void yyfree (void * ptr )
+{
+			free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+/* %if-tables-serialization definitions */
+/* %define-yytables   The name for this specific scanner's tables. */
+/* %endif */
+
+/* %ok-for-header */
+
+yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str(), NULL, 10);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str() + 2, NULL, 16);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "hex is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str()+2, NULL, 2);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "binary is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+void pio_assembler::scan_begin ()
+{
+  yy_flex_debug = false;
+  if (source.empty () || source == "-")
+    yyin = stdin;
+  else if (!(yyin = fopen (source.c_str (), "r")))
+    {
+      std::cerr << "cannot open " << source << ": " << strerror(errno) << '\n';
+      exit (EXIT_FAILURE);
+    }
+}
+
+void pio_assembler::scan_end ()
+{
+  fclose (yyin);
+}
+
diff --git a/tools/pioasm/gen/location.h b/tools/pioasm/gen/location.h
new file mode 100644
index 0000000..4965682
--- /dev/null
+++ b/tools/pioasm/gen/location.h
@@ -0,0 +1,302 @@
+// A Bison parser, made by GNU Bison 3.7.2.
+
+// Locations for Bison parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton.  Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+/**
+ ** \file pico_sdk/tools/pioasm/gen/location.h
+ ** Define the yy::location class.
+ */
+
+#ifndef YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_LOCATION_H_INCLUDED
+# define YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_LOCATION_H_INCLUDED
+
+# include <iostream>
+# include <string>
+
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
+
+namespace yy {
+
+  /// A point in a source file.
+  class position
+  {
+  public:
+    /// Type for file name.
+    typedef const std::string filename_type;
+    /// Type for line and column numbers.
+    typedef int counter_type;
+
+    /// Construct a position.
+    explicit position (filename_type* f = YY_NULLPTR,
+                       counter_type l = 1,
+                       counter_type c = 1)
+      : filename (f)
+      , line (l)
+      , column (c)
+    {}
+
+
+    /// Initialization.
+    void initialize (filename_type* fn = YY_NULLPTR,
+                     counter_type l = 1,
+                     counter_type c = 1)
+    {
+      filename = fn;
+      line = l;
+      column = c;
+    }
+
+    /** \name Line and Column related manipulators
+     ** \{ */
+    /// (line related) Advance to the COUNT next lines.
+    void lines (counter_type count = 1)
+    {
+      if (count)
+        {
+          column = 1;
+          line = add_ (line, count, 1);
+        }
+    }
+
+    /// (column related) Advance to the COUNT next columns.
+    void columns (counter_type count = 1)
+    {
+      column = add_ (column, count, 1);
+    }
+    /** \} */
+
+    /// File name to which this position refers.
+    filename_type* filename;
+    /// Current line number.
+    counter_type line;
+    /// Current column number.
+    counter_type column;
+
+  private:
+    /// Compute max (min, lhs+rhs).
+    static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min)
+    {
+      return lhs + rhs < min ? min : lhs + rhs;
+    }
+  };
+
+  /// Add \a width columns, in place.
+  inline position&
+  operator+= (position& res, position::counter_type width)
+  {
+    res.columns (width);
+    return res;
+  }
+
+  /// Add \a width columns.
+  inline position
+  operator+ (position res, position::counter_type width)
+  {
+    return res += width;
+  }
+
+  /// Subtract \a width columns, in place.
+  inline position&
+  operator-= (position& res, position::counter_type width)
+  {
+    return res += -width;
+  }
+
+  /// Subtract \a width columns.
+  inline position
+  operator- (position res, position::counter_type width)
+  {
+    return res -= width;
+  }
+
+  /** \brief Intercept output stream redirection.
+   ** \param ostr the destination output stream
+   ** \param pos a reference to the position to redirect
+   */
+  template <typename YYChar>
+  std::basic_ostream<YYChar>&
+  operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
+  {
+    if (pos.filename)
+      ostr << *pos.filename << ':';
+    return ostr << pos.line << '.' << pos.column;
+  }
+
+  /// Two points in a source file.
+  class location
+  {
+  public:
+    /// Type for file name.
+    typedef position::filename_type filename_type;
+    /// Type for line and column numbers.
+    typedef position::counter_type counter_type;
+
+    /// Construct a location from \a b to \a e.
+    location (const position& b, const position& e)
+      : begin (b)
+      , end (e)
+    {}
+
+    /// Construct a 0-width location in \a p.
+    explicit location (const position& p = position ())
+      : begin (p)
+      , end (p)
+    {}
+
+    /// Construct a 0-width location in \a f, \a l, \a c.
+    explicit location (filename_type* f,
+                       counter_type l = 1,
+                       counter_type c = 1)
+      : begin (f, l, c)
+      , end (f, l, c)
+    {}
+
+
+    /// Initialization.
+    void initialize (filename_type* f = YY_NULLPTR,
+                     counter_type l = 1,
+                     counter_type c = 1)
+    {
+      begin.initialize (f, l, c);
+      end = begin;
+    }
+
+    /** \name Line and Column related manipulators
+     ** \{ */
+  public:
+    /// Reset initial location to final location.
+    void step ()
+    {
+      begin = end;
+    }
+
+    /// Extend the current location to the COUNT next columns.
+    void columns (counter_type count = 1)
+    {
+      end += count;
+    }
+
+    /// Extend the current location to the COUNT next lines.
+    void lines (counter_type count = 1)
+    {
+      end.lines (count);
+    }
+    /** \} */
+
+
+  public:
+    /// Beginning of the located region.
+    position begin;
+    /// End of the located region.
+    position end;
+  };
+
+  /// Join two locations, in place.
+  inline location&
+  operator+= (location& res, const location& end)
+  {
+    res.end = end.end;
+    return res;
+  }
+
+  /// Join two locations.
+  inline location
+  operator+ (location res, const location& end)
+  {
+    return res += end;
+  }
+
+  /// Add \a width columns to the end position, in place.
+  inline location&
+  operator+= (location& res, location::counter_type width)
+  {
+    res.columns (width);
+    return res;
+  }
+
+  /// Add \a width columns to the end position.
+  inline location
+  operator+ (location res, location::counter_type width)
+  {
+    return res += width;
+  }
+
+  /// Subtract \a width columns to the end position, in place.
+  inline location&
+  operator-= (location& res, location::counter_type width)
+  {
+    return res += -width;
+  }
+
+  /// Subtract \a width columns to the end position.
+  inline location
+  operator- (location res, location::counter_type width)
+  {
+    return res -= width;
+  }
+
+  /** \brief Intercept output stream redirection.
+   ** \param ostr the destination output stream
+   ** \param loc a reference to the location to redirect
+   **
+   ** Avoid duplicate information.
+   */
+  template <typename YYChar>
+  std::basic_ostream<YYChar>&
+  operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
+  {
+    location::counter_type end_col
+      = 0 < loc.end.column ? loc.end.column - 1 : 0;
+    ostr << loc.begin;
+    if (loc.end.filename
+        && (!loc.begin.filename
+            || *loc.begin.filename != *loc.end.filename))
+      ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col;
+    else if (loc.begin.line < loc.end.line)
+      ostr << '-' << loc.end.line << '.' << end_col;
+    else if (loc.begin.column < end_col)
+      ostr << '-' << end_col;
+    return ostr;
+  }
+
+} // yy
+
+#endif // !YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_LOCATION_H_INCLUDED
diff --git a/tools/pioasm/gen/parser.cpp b/tools/pioasm/gen/parser.cpp
new file mode 100644
index 0000000..c9bd036
--- /dev/null
+++ b/tools/pioasm/gen/parser.cpp
@@ -0,0 +1,2208 @@
+// A Bison parser, made by GNU Bison 3.7.2.
+
+// Skeleton implementation for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton.  Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_.  They are
+// private implementation details that can be changed or removed.
+
+
+
+
+
+#include "parser.hpp"
+
+
+// Unqualified %code blocks.
+
+    #include "pio_assembler.h"
+  #ifdef _MSC_VER
+  #pragma warning(disable : 4244) // possible loss of data (valid warning, but there is a software check / missing cast)
+  #endif
+
+
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE.
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+
+// Whether we are compiled with exception support.
+#ifndef YY_EXCEPTIONS
+# if defined __GNUC__ && !defined __EXCEPTIONS
+#  define YY_EXCEPTIONS 0
+# else
+#  define YY_EXCEPTIONS 1
+# endif
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K].location)
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+# ifndef YYLLOC_DEFAULT
+#  define YYLLOC_DEFAULT(Current, Rhs, N)                               \
+    do                                                                  \
+      if (N)                                                            \
+        {                                                               \
+          (Current).begin  = YYRHSLOC (Rhs, 1).begin;                   \
+          (Current).end    = YYRHSLOC (Rhs, N).end;                     \
+        }                                                               \
+      else                                                              \
+        {                                                               \
+          (Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end;      \
+        }                                                               \
+    while (false)
+# endif
+
+
+// Enable debugging if requested.
+#if YYDEBUG
+
+// A pseudo ostream that takes yydebug_ into account.
+# define YYCDEBUG if (yydebug_) (*yycdebug_)
+
+# define YY_SYMBOL_PRINT(Title, Symbol)         \
+  do {                                          \
+    if (yydebug_)                               \
+    {                                           \
+      *yycdebug_ << Title << ' ';               \
+      yy_print_ (*yycdebug_, Symbol);           \
+      *yycdebug_ << '\n';                       \
+    }                                           \
+  } while (false)
+
+# define YY_REDUCE_PRINT(Rule)          \
+  do {                                  \
+    if (yydebug_)                       \
+      yy_reduce_print_ (Rule);          \
+  } while (false)
+
+# define YY_STACK_PRINT()               \
+  do {                                  \
+    if (yydebug_)                       \
+      yy_stack_print_ ();                \
+  } while (false)
+
+#else // !YYDEBUG
+
+# define YYCDEBUG if (false) std::cerr
+# define YY_SYMBOL_PRINT(Title, Symbol)  YYUSE (Symbol)
+# define YY_REDUCE_PRINT(Rule)           static_cast<void> (0)
+# define YY_STACK_PRINT()                static_cast<void> (0)
+
+#endif // !YYDEBUG
+
+#define yyerrok         (yyerrstatus_ = 0)
+#define yyclearin       (yyla.clear ())
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+#define YYRECOVERING()  (!!yyerrstatus_)
+
+namespace yy {
+
+  /// Build a parser object.
+  parser::parser (pio_assembler& pioasm_yyarg)
+#if YYDEBUG
+    : yydebug_ (false),
+      yycdebug_ (&std::cerr),
+#else
+    :
+#endif
+      yy_lac_established_ (false),
+      pioasm (pioasm_yyarg)
+  {}
+
+  parser::~parser ()
+  {}
+
+  parser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW
+  {}
+
+  /*---------------.
+  | symbol kinds.  |
+  `---------------*/
+
+
+
+  // by_state.
+  parser::by_state::by_state () YY_NOEXCEPT
+    : state (empty_state)
+  {}
+
+  parser::by_state::by_state (const by_state& that) YY_NOEXCEPT
+    : state (that.state)
+  {}
+
+  void
+  parser::by_state::clear () YY_NOEXCEPT
+  {
+    state = empty_state;
+  }
+
+  void
+  parser::by_state::move (by_state& that)
+  {
+    state = that.state;
+    that.clear ();
+  }
+
+  parser::by_state::by_state (state_type s) YY_NOEXCEPT
+    : state (s)
+  {}
+
+  parser::symbol_kind_type
+  parser::by_state::kind () const YY_NOEXCEPT
+  {
+    if (state == empty_state)
+      return symbol_kind::S_YYEMPTY;
+    else
+      return YY_CAST (symbol_kind_type, yystos_[+state]);
+  }
+
+  parser::stack_symbol_type::stack_symbol_type ()
+  {}
+
+  parser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that)
+    : super_type (YY_MOVE (that.state), YY_MOVE (that.location))
+  {
+    switch (that.kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.YY_MOVE_OR_COPY< bool > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.YY_MOVE_OR_COPY< enum condition > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.YY_MOVE_OR_COPY< enum in_out_set > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.YY_MOVE_OR_COPY< enum irq > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.YY_MOVE_OR_COPY< enum mov > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.YY_MOVE_OR_COPY< enum mov_op > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.YY_MOVE_OR_COPY< int > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.YY_MOVE_OR_COPY< std::shared_ptr<instruction> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.YY_MOVE_OR_COPY< std::shared_ptr<resolvable> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.YY_MOVE_OR_COPY< std::shared_ptr<symbol> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.YY_MOVE_OR_COPY< std::shared_ptr<wait_source> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.YY_MOVE_OR_COPY< std::string > (YY_MOVE (that.value));
+        break;
+
+      default:
+        break;
+    }
+
+#if 201103L <= YY_CPLUSPLUS
+    // that is emptied.
+    that.state = empty_state;
+#endif
+  }
+
+  parser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that)
+    : super_type (s, YY_MOVE (that.location))
+  {
+    switch (that.kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.move< bool > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.move< enum condition > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.move< enum in_out_set > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.move< enum irq > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.move< enum mov > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.move< enum mov_op > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.move< int > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.move< std::shared_ptr<instruction> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.move< std::shared_ptr<resolvable> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.move< std::shared_ptr<symbol> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.move< std::shared_ptr<wait_source> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.move< std::string > (YY_MOVE (that.value));
+        break;
+
+      default:
+        break;
+    }
+
+    // that is emptied.
+    that.kind_ = symbol_kind::S_YYEMPTY;
+  }
+
+#if YY_CPLUSPLUS < 201103L
+  parser::stack_symbol_type&
+  parser::stack_symbol_type::operator= (const stack_symbol_type& that)
+  {
+    state = that.state;
+    switch (that.kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.copy< bool > (that.value);
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.copy< enum condition > (that.value);
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.copy< enum in_out_set > (that.value);
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.copy< enum irq > (that.value);
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.copy< enum mov > (that.value);
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.copy< enum mov_op > (that.value);
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.copy< int > (that.value);
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.copy< std::shared_ptr<instruction> > (that.value);
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.copy< std::shared_ptr<resolvable> > (that.value);
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.copy< std::shared_ptr<symbol> > (that.value);
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.copy< std::shared_ptr<wait_source> > (that.value);
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.copy< std::string > (that.value);
+        break;
+
+      default:
+        break;
+    }
+
+    location = that.location;
+    return *this;
+  }
+
+  parser::stack_symbol_type&
+  parser::stack_symbol_type::operator= (stack_symbol_type& that)
+  {
+    state = that.state;
+    switch (that.kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.move< bool > (that.value);
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.move< enum condition > (that.value);
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.move< enum in_out_set > (that.value);
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.move< enum irq > (that.value);
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.move< enum mov > (that.value);
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.move< enum mov_op > (that.value);
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.move< int > (that.value);
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.move< std::shared_ptr<instruction> > (that.value);
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.move< std::shared_ptr<resolvable> > (that.value);
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.move< std::shared_ptr<symbol> > (that.value);
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.move< std::shared_ptr<wait_source> > (that.value);
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.move< std::string > (that.value);
+        break;
+
+      default:
+        break;
+    }
+
+    location = that.location;
+    // that is emptied.
+    that.state = empty_state;
+    return *this;
+  }
+#endif
+
+  template <typename Base>
+  void
+  parser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const
+  {
+    if (yymsg)
+      YY_SYMBOL_PRINT (yymsg, yysym);
+  }
+
+#if YYDEBUG
+  template <typename Base>
+  void
+  parser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const
+  {
+    std::ostream& yyoutput = yyo;
+    YYUSE (yyoutput);
+    if (yysym.empty ())
+      yyo << "empty symbol";
+    else
+      {
+        symbol_kind_type yykind = yysym.kind ();
+        yyo << (yykind < YYNTOKENS ? "token" : "nterm")
+            << ' ' << yysym.name () << " ("
+            << yysym.location << ": ";
+        switch (yykind)
+    {
+      case symbol_kind::S_ID: // "identifier"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_STRING: // "string"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_NON_WS: // "text"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_value: // value
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_expression: // expression
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_base_instruction: // base_instruction
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_delay: // delay
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_sideset: // sideset
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_condition: // condition
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_out_target: // out_target
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_mov_source: // mov_source
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_set_target: // set_target
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_if_full: // if_full
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_if_empty: // if_empty
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_blocking: // blocking
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+                 { yyo << "..."; }
+        break;
+
+      case symbol_kind::S_symbol_def: // symbol_def
+                 { yyo << "..."; }
+        break;
+
+      default:
+        break;
+    }
+        yyo << ')';
+      }
+  }
+#endif
+
+  void
+  parser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym)
+  {
+    if (m)
+      YY_SYMBOL_PRINT (m, sym);
+    yystack_.push (YY_MOVE (sym));
+  }
+
+  void
+  parser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym)
+  {
+#if 201103L <= YY_CPLUSPLUS
+    yypush_ (m, stack_symbol_type (s, std::move (sym)));
+#else
+    stack_symbol_type ss (s, sym);
+    yypush_ (m, ss);
+#endif
+  }
+
+  void
+  parser::yypop_ (int n)
+  {
+    yystack_.pop (n);
+  }
+
+#if YYDEBUG
+  std::ostream&
+  parser::debug_stream () const
+  {
+    return *yycdebug_;
+  }
+
+  void
+  parser::set_debug_stream (std::ostream& o)
+  {
+    yycdebug_ = &o;
+  }
+
+
+  parser::debug_level_type
+  parser::debug_level () const
+  {
+    return yydebug_;
+  }
+
+  void
+  parser::set_debug_level (debug_level_type l)
+  {
+    yydebug_ = l;
+  }
+#endif // YYDEBUG
+
+  parser::state_type
+  parser::yy_lr_goto_state_ (state_type yystate, int yysym)
+  {
+    int yyr = yypgoto_[yysym - YYNTOKENS] + yystate;
+    if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate)
+      return yytable_[yyr];
+    else
+      return yydefgoto_[yysym - YYNTOKENS];
+  }
+
+  bool
+  parser::yy_pact_value_is_default_ (int yyvalue)
+  {
+    return yyvalue == yypact_ninf_;
+  }
+
+  bool
+  parser::yy_table_value_is_error_ (int yyvalue)
+  {
+    return yyvalue == yytable_ninf_;
+  }
+
+  int
+  parser::operator() ()
+  {
+    return parse ();
+  }
+
+  int
+  parser::parse ()
+  {
+    int yyn;
+    /// Length of the RHS of the rule being reduced.
+    int yylen = 0;
+
+    // Error handling.
+    int yynerrs_ = 0;
+    int yyerrstatus_ = 0;
+
+    /// The lookahead symbol.
+    symbol_type yyla;
+
+    /// The locations where the error started and ended.
+    stack_symbol_type yyerror_range[3];
+
+    /// The return value of parse ().
+    int yyresult;
+
+    /// Discard the LAC context in case there still is one left from a
+    /// previous invocation.
+    yy_lac_discard_ ("init");
+
+#if YY_EXCEPTIONS
+    try
+#endif // YY_EXCEPTIONS
+      {
+    YYCDEBUG << "Starting parse\n";
+
+
+    /* Initialize the stack.  The initial state will be set in
+       yynewstate, since the latter expects the semantical and the
+       location values to have been already stored, initialize these
+       stacks with a primary value.  */
+    yystack_.clear ();
+    yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla));
+
+  /*-----------------------------------------------.
+  | yynewstate -- push a new symbol on the stack.  |
+  `-----------------------------------------------*/
+  yynewstate:
+    YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n';
+    YY_STACK_PRINT ();
+
+    // Accept?
+    if (yystack_[0].state == yyfinal_)
+      YYACCEPT;
+
+    goto yybackup;
+
+
+  /*-----------.
+  | yybackup.  |
+  `-----------*/
+  yybackup:
+    // Try to take a decision without lookahead.
+    yyn = yypact_[+yystack_[0].state];
+    if (yy_pact_value_is_default_ (yyn))
+      goto yydefault;
+
+    // Read a lookahead token.
+    if (yyla.empty ())
+      {
+        YYCDEBUG << "Reading a token\n";
+#if YY_EXCEPTIONS
+        try
+#endif // YY_EXCEPTIONS
+          {
+            symbol_type yylookahead (yylex (pioasm));
+            yyla.move (yylookahead);
+          }
+#if YY_EXCEPTIONS
+        catch (const syntax_error& yyexc)
+          {
+            YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+            error (yyexc);
+            goto yyerrlab1;
+          }
+#endif // YY_EXCEPTIONS
+      }
+    YY_SYMBOL_PRINT ("Next token is", yyla);
+
+    if (yyla.kind () == symbol_kind::S_YYerror)
+    {
+      // The scanner already issued an error message, process directly
+      // to error recovery.  But do not keep the error token as
+      // lookahead, it is too special and may lead us to an endless
+      // loop in error recovery. */
+      yyla.kind_ = symbol_kind::S_YYUNDEF;
+      goto yyerrlab1;
+    }
+
+    /* If the proper action on seeing token YYLA.TYPE is to reduce or
+       to detect an error, take that action.  */
+    yyn += yyla.kind ();
+    if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ())
+      {
+        if (!yy_lac_establish_ (yyla.kind ()))
+           goto yyerrlab;
+        goto yydefault;
+      }
+
+    // Reduce or error.
+    yyn = yytable_[yyn];
+    if (yyn <= 0)
+      {
+        if (yy_table_value_is_error_ (yyn))
+          goto yyerrlab;
+        if (!yy_lac_establish_ (yyla.kind ()))
+           goto yyerrlab;
+
+        yyn = -yyn;
+        goto yyreduce;
+      }
+
+    // Count tokens shifted since error; after three, turn off error status.
+    if (yyerrstatus_)
+      --yyerrstatus_;
+
+    // Shift the lookahead token.
+    yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla));
+    yy_lac_discard_ ("shift");
+    goto yynewstate;
+
+
+  /*-----------------------------------------------------------.
+  | yydefault -- do the default action for the current state.  |
+  `-----------------------------------------------------------*/
+  yydefault:
+    yyn = yydefact_[+yystack_[0].state];
+    if (yyn == 0)
+      goto yyerrlab;
+    goto yyreduce;
+
+
+  /*-----------------------------.
+  | yyreduce -- do a reduction.  |
+  `-----------------------------*/
+  yyreduce:
+    yylen = yyr2_[yyn];
+    {
+      stack_symbol_type yylhs;
+      yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]);
+      /* Variants are always initialized to an empty instance of the
+         correct type. The default '$$ = $1' action is NOT applied
+         when using variants.  */
+      switch (yyr1_[yyn])
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        yylhs.value.emplace< bool > ();
+        break;
+
+      case symbol_kind::S_condition: // condition
+        yylhs.value.emplace< enum condition > ();
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        yylhs.value.emplace< enum in_out_set > ();
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        yylhs.value.emplace< enum irq > ();
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        yylhs.value.emplace< enum mov > ();
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        yylhs.value.emplace< enum mov_op > ();
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        yylhs.value.emplace< int > ();
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        yylhs.value.emplace< std::shared_ptr<instruction> > ();
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        yylhs.value.emplace< std::shared_ptr<resolvable> > ();
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        yylhs.value.emplace< std::shared_ptr<symbol> > ();
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        yylhs.value.emplace< std::shared_ptr<wait_source> > ();
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        yylhs.value.emplace< std::string > ();
+        break;
+
+      default:
+        break;
+    }
+
+
+      // Default location.
+      {
+        stack_type::slice range (yystack_, yylen);
+        YYLLOC_DEFAULT (yylhs.location, range, yylen);
+        yyerror_range[1].location = yylhs.location;
+      }
+
+      // Perform the reduction.
+      YY_REDUCE_PRINT (yyn);
+#if YY_EXCEPTIONS
+      try
+#endif // YY_EXCEPTIONS
+        {
+          switch (yyn)
+            {
+  case 2: // file: lines "end of file"
+              { if (pioasm.error_count || pioasm.write_output()) YYABORT; }
+    break;
+
+  case 5: // line: ".program" "identifier"
+                                                { if (!pioasm.add_program(yylhs.location, yystack_[0].value.as < std::string > ())) { std::stringstream msg; msg << "program " << yystack_[0].value.as < std::string > () << " already exists"; error(yylhs.location, msg.str()); abort(); } }
+    break;
+
+  case 7: // line: instruction
+                                                { pioasm.get_current_program(yystack_[0].location, "instruction").add_instruction(yystack_[0].value.as < std::shared_ptr<instruction> > ()); }
+    break;
+
+  case 8: // line: label_decl instruction
+                                                { auto &p = pioasm.get_current_program(yystack_[0].location, "instruction"); p.add_label(yystack_[1].value.as < std::shared_ptr<symbol> > ()); p.add_instruction(yystack_[0].value.as < std::shared_ptr<instruction> > ()); }
+    break;
+
+  case 9: // line: label_decl
+                                                { pioasm.get_current_program(yystack_[0].location, "label").add_label(yystack_[0].value.as < std::shared_ptr<symbol> > ()); }
+    break;
+
+  case 12: // line: error
+                                                { if (pioasm.error_count > 6) { std::cerr << "\ntoo many errors; aborting.\n"; YYABORT; } }
+    break;
+
+  case 13: // code_block: "code block" "%}"
+                                                { std::string of = yystack_[1].value.as < std::string > (); if (of.empty()) of = output_format::default_name; pioasm.get_current_program(yylhs.location, "code block", false, false).add_code_block( code_block(yylhs.location, of, yystack_[0].value.as < std::string > ())); }
+    break;
+
+  case 14: // label_decl: symbol_def ":"
+                            { yystack_[1].value.as < std::shared_ptr<symbol> > ()->is_label = true; yylhs.value.as < std::shared_ptr<symbol> > () = yystack_[1].value.as < std::shared_ptr<symbol> > (); }
+    break;
+
+  case 15: // directive: ".define" symbol_def expression
+                                      { yystack_[1].value.as < std::shared_ptr<symbol> > ()->is_label = false; yystack_[1].value.as < std::shared_ptr<symbol> > ()->value = yystack_[0].value.as < std::shared_ptr<resolvable> > (); pioasm.get_current_program(yystack_[2].location, ".define", false, false).add_symbol(yystack_[1].value.as < std::shared_ptr<symbol> > ()); }
+    break;
+
+  case 16: // directive: ".origin" value
+                                      { pioasm.get_current_program(yystack_[1].location, ".origin", true).set_origin(yylhs.location, yystack_[0].value.as < std::shared_ptr<resolvable> > ()); }
+    break;
+
+  case 17: // directive: ".side_set" value "opt" "pindirs"
+                                      { pioasm.get_current_program(yystack_[3].location, ".side_set", true).set_sideset(yylhs.location, yystack_[2].value.as < std::shared_ptr<resolvable> > (), true, true); }
+    break;
+
+  case 18: // directive: ".side_set" value "opt"
+                                      { pioasm.get_current_program(yystack_[2].location, ".side_set", true).set_sideset(yylhs.location, yystack_[1].value.as < std::shared_ptr<resolvable> > (), true, false); }
+    break;
+
+  case 19: // directive: ".side_set" value "pindirs"
+                                      { pioasm.get_current_program(yystack_[2].location, ".side_set", true).set_sideset(yylhs.location, yystack_[1].value.as < std::shared_ptr<resolvable> > (), false, true); }
+    break;
+
+  case 20: // directive: ".side_set" value
+                                      { pioasm.get_current_program(yystack_[1].location, ".side_set", true).set_sideset(yylhs.location, yystack_[0].value.as < std::shared_ptr<resolvable> > (), false, false); }
+    break;
+
+  case 21: // directive: ".wrap_target"
+                                      { pioasm.get_current_program(yystack_[0].location, ".wrap_target").set_wrap_target(yylhs.location); }
+    break;
+
+  case 22: // directive: ".wrap"
+                                      { pioasm.get_current_program(yystack_[0].location, ".wrap").set_wrap(yylhs.location); }
+    break;
+
+  case 23: // directive: ".word" value
+                                      { pioasm.get_current_program(yystack_[1].location, "instruction").add_instruction(std::shared_ptr<instruction>(new instr_word(yylhs.location, yystack_[0].value.as < std::shared_ptr<resolvable> > ()))); }
+    break;
+
+  case 24: // directive: ".lang_opt" "text" "text" "=" "integer"
+                                      { pioasm.get_current_program(yystack_[4].location, ".lang_opt").add_lang_opt(yystack_[3].value.as < std::string > (), yystack_[2].value.as < std::string > (), std::to_string(yystack_[0].value.as < int > ())); }
+    break;
+
+  case 25: // directive: ".lang_opt" "text" "text" "=" "string"
+                                        { pioasm.get_current_program(yystack_[4].location, ".lang_opt").add_lang_opt(yystack_[3].value.as < std::string > (), yystack_[2].value.as < std::string > (), yystack_[0].value.as < std::string > ()); }
+    break;
+
+  case 26: // directive: ".lang_opt" "text" "text" "=" "text"
+                                        { pioasm.get_current_program(yystack_[4].location, ".lang_opt").add_lang_opt(yystack_[3].value.as < std::string > (), yystack_[2].value.as < std::string > (), yystack_[0].value.as < std::string > ()); }
+    break;
+
+  case 27: // directive: ".lang_opt" error
+                                      { error(yylhs.location, "expected format is .lang_opt language option_name = option_value"); }
+    break;
+
+  case 28: // directive: UNKNOWN_DIRECTIVE
+                                      { std::stringstream msg; msg << "unknown directive " << yystack_[0].value.as < std::string > (); throw syntax_error(yylhs.location, msg.str()); }
+    break;
+
+  case 29: // value: "integer"
+           { yylhs.value.as < std::shared_ptr<resolvable> > () = resolvable_int(yylhs.location, yystack_[0].value.as < int > ()); }
+    break;
+
+  case 30: // value: "identifier"
+          { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<resolvable>(new name_ref(yylhs.location, yystack_[0].value.as < std::string > ())); }
+    break;
+
+  case 31: // value: "(" expression ")"
+                                { yylhs.value.as < std::shared_ptr<resolvable> > () = yystack_[1].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 32: // expression: value
+     { yylhs.value.as < std::shared_ptr<resolvable> > () = yystack_[0].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 33: // expression: expression "+" expression
+                                  { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::add, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 34: // expression: expression "-" expression
+                                   { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::subtract, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 35: // expression: expression "*" expression
+                                      { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::multiply, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ()));  }
+    break;
+
+  case 36: // expression: expression "/" expression
+                                    { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::divide, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 37: // expression: expression "|" expression
+                                { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::or_, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 38: // expression: expression "&" expression
+                                 { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::and_, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 39: // expression: expression "^" expression
+                                 { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<binary_operation>(new binary_operation(yylhs.location, binary_operation::xor_, yystack_[2].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 40: // expression: "-" expression
+                        { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<unary_operation>(new unary_operation(yylhs.location, unary_operation::negate, yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 41: // expression: "::" expression
+                          { yylhs.value.as < std::shared_ptr<resolvable> > () = std::shared_ptr<unary_operation>(new unary_operation(yylhs.location, unary_operation::reverse, yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 42: // instruction: base_instruction sideset delay
+                                   { yylhs.value.as < std::shared_ptr<instruction> > () = yystack_[2].value.as < std::shared_ptr<instruction> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->sideset = yystack_[1].value.as < std::shared_ptr<resolvable> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->delay = yystack_[0].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 43: // instruction: base_instruction delay sideset
+                                   { yylhs.value.as < std::shared_ptr<instruction> > () = yystack_[2].value.as < std::shared_ptr<instruction> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->delay = yystack_[1].value.as < std::shared_ptr<resolvable> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->sideset = yystack_[0].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 44: // instruction: base_instruction sideset
+                             { yylhs.value.as < std::shared_ptr<instruction> > () = yystack_[1].value.as < std::shared_ptr<instruction> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->sideset = yystack_[0].value.as < std::shared_ptr<resolvable> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->delay = resolvable_int(yylhs.location, 0); }
+    break;
+
+  case 45: // instruction: base_instruction delay
+                           { yylhs.value.as < std::shared_ptr<instruction> > () = yystack_[1].value.as < std::shared_ptr<instruction> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->delay = yystack_[0].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 46: // instruction: base_instruction
+                     { yylhs.value.as < std::shared_ptr<instruction> > () = yystack_[0].value.as < std::shared_ptr<instruction> > (); yylhs.value.as < std::shared_ptr<instruction> > ()->delay = resolvable_int(yylhs.location, 0); }
+    break;
+
+  case 47: // base_instruction: "nop"
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_nop(yylhs.location)); }
+    break;
+
+  case 48: // base_instruction: "jmp" condition comma expression
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_jmp(yylhs.location, yystack_[2].value.as < enum condition > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 49: // base_instruction: "wait" value wait_source
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_wait(yylhs.location, yystack_[1].value.as < std::shared_ptr<resolvable> > (), yystack_[0].value.as < std::shared_ptr<wait_source> > ())); }
+    break;
+
+  case 50: // base_instruction: "wait" value "," value
+                                                          { std::stringstream msg; location l; l.begin = yystack_[2].location.end; l.end = yystack_[1].location.end; msg << "expected irq, gpio or pin after the polarity value and before the \",\""; throw yy::parser::syntax_error(l, msg.str()); }
+    break;
+
+  case 51: // base_instruction: "wait" wait_source
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_wait(yylhs.location, resolvable_int(yylhs.location, 1),  yystack_[0].value.as < std::shared_ptr<wait_source> > ())); }
+    break;
+
+  case 52: // base_instruction: "in" in_source comma value
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_in(yylhs.location, yystack_[2].value.as < enum in_out_set > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 53: // base_instruction: "out" out_target comma value
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_out(yylhs.location, yystack_[2].value.as < enum in_out_set > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 54: // base_instruction: "push" if_full blocking
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_push(yylhs.location, yystack_[1].value.as < bool > (), yystack_[0].value.as < bool > ())); }
+    break;
+
+  case 55: // base_instruction: "pull" if_empty blocking
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_pull(yylhs.location, yystack_[1].value.as < bool > (), yystack_[0].value.as < bool > ())); }
+    break;
+
+  case 56: // base_instruction: "mov" mov_target comma mov_op mov_source
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_mov(yylhs.location, yystack_[3].value.as < enum mov > (), yystack_[0].value.as < enum mov > (), yystack_[1].value.as < enum mov_op > ())); }
+    break;
+
+  case 57: // base_instruction: "irq" irq_modifiers value "rel"
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_irq(yylhs.location, yystack_[2].value.as < enum irq > (), yystack_[1].value.as < std::shared_ptr<resolvable> > (), true)); }
+    break;
+
+  case 58: // base_instruction: "irq" irq_modifiers value
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_irq(yylhs.location, yystack_[1].value.as < enum irq > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 59: // base_instruction: "set" set_target comma value
+                                                          { yylhs.value.as < std::shared_ptr<instruction> > () = std::shared_ptr<instruction>(new instr_set(yylhs.location, yystack_[2].value.as < enum in_out_set > (), yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 60: // delay: "[" expression "]"
+                                 { yylhs.value.as < std::shared_ptr<resolvable> > () = yystack_[1].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 61: // sideset: "side" value
+               { yylhs.value.as < std::shared_ptr<resolvable> > () = yystack_[0].value.as < std::shared_ptr<resolvable> > (); }
+    break;
+
+  case 62: // condition: "!" "x"
+                            { yylhs.value.as < enum condition > () = condition::xz; }
+    break;
+
+  case 63: // condition: "x" "--"
+                            { yylhs.value.as < enum condition > () = condition::xnz__; }
+    break;
+
+  case 64: // condition: "!" "y"
+                            { yylhs.value.as < enum condition > () = condition::yz; }
+    break;
+
+  case 65: // condition: "y" "--"
+                            { yylhs.value.as < enum condition > () = condition::ynz__; }
+    break;
+
+  case 66: // condition: "x" "!=" "y"
+                            { yylhs.value.as < enum condition > () = condition::xney; }
+    break;
+
+  case 67: // condition: "pin"
+                            { yylhs.value.as < enum condition > () = condition::pin; }
+    break;
+
+  case 68: // condition: "!" "osre"
+                            { yylhs.value.as < enum condition > () = condition::osrez; }
+    break;
+
+  case 69: // condition: %empty
+                            { yylhs.value.as < enum condition > () = condition::al; }
+    break;
+
+  case 70: // wait_source: "irq" comma value "rel"
+                            { yylhs.value.as < std::shared_ptr<wait_source> > () = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, yystack_[1].value.as < std::shared_ptr<resolvable> > (), true)); }
+    break;
+
+  case 71: // wait_source: "irq" comma value
+                            { yylhs.value.as < std::shared_ptr<wait_source> > () = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, yystack_[0].value.as < std::shared_ptr<resolvable> > (), false)); }
+    break;
+
+  case 72: // wait_source: "gpio" comma value
+                            { yylhs.value.as < std::shared_ptr<wait_source> > () = std::shared_ptr<wait_source>(new wait_source(wait_source::gpio, yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 73: // wait_source: "pin" comma value
+                            { yylhs.value.as < std::shared_ptr<wait_source> > () = std::shared_ptr<wait_source>(new wait_source(wait_source::pin, yystack_[0].value.as < std::shared_ptr<resolvable> > ())); }
+    break;
+
+  case 76: // in_source: "pins"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_pins; }
+    break;
+
+  case 77: // in_source: "x"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_x; }
+    break;
+
+  case 78: // in_source: "y"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_y; }
+    break;
+
+  case 79: // in_source: "null"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_null; }
+    break;
+
+  case 80: // in_source: "isr"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_isr; }
+    break;
+
+  case 81: // in_source: "osr"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_osr; }
+    break;
+
+  case 82: // in_source: "status"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_status; }
+    break;
+
+  case 83: // out_target: "pins"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_pins; }
+    break;
+
+  case 84: // out_target: "x"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_x; }
+    break;
+
+  case 85: // out_target: "y"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_y; }
+    break;
+
+  case 86: // out_target: "null"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_null; }
+    break;
+
+  case 87: // out_target: "pindirs"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_pindirs; }
+    break;
+
+  case 88: // out_target: "isr"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_isr; }
+    break;
+
+  case 89: // out_target: "pc"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::out_set_pc; }
+    break;
+
+  case 90: // out_target: "exec"
+                 { yylhs.value.as < enum in_out_set > () = in_out_set::out_exec; }
+    break;
+
+  case 91: // mov_target: "pins"
+                 { yylhs.value.as < enum mov > () = mov::pins; }
+    break;
+
+  case 92: // mov_target: "x"
+                 { yylhs.value.as < enum mov > () = mov::x; }
+    break;
+
+  case 93: // mov_target: "y"
+                 { yylhs.value.as < enum mov > () = mov::y; }
+    break;
+
+  case 94: // mov_target: "exec"
+                 { yylhs.value.as < enum mov > () = mov::exec; }
+    break;
+
+  case 95: // mov_target: "pc"
+                 { yylhs.value.as < enum mov > () = mov::pc; }
+    break;
+
+  case 96: // mov_target: "isr"
+                 { yylhs.value.as < enum mov > () = mov::isr; }
+    break;
+
+  case 97: // mov_target: "osr"
+                 { yylhs.value.as < enum mov > () = mov::osr; }
+    break;
+
+  case 98: // mov_source: "pins"
+                 { yylhs.value.as < enum mov > () = mov::pins; }
+    break;
+
+  case 99: // mov_source: "x"
+                 { yylhs.value.as < enum mov > () = mov::x; }
+    break;
+
+  case 100: // mov_source: "y"
+                 { yylhs.value.as < enum mov > () = mov::y; }
+    break;
+
+  case 101: // mov_source: "null"
+                 { yylhs.value.as < enum mov > () = mov::null; }
+    break;
+
+  case 102: // mov_source: "status"
+                 { yylhs.value.as < enum mov > () = mov::status; }
+    break;
+
+  case 103: // mov_source: "isr"
+                 { yylhs.value.as < enum mov > () = mov::isr; }
+    break;
+
+  case 104: // mov_source: "osr"
+                 { yylhs.value.as < enum mov > () = mov::osr; }
+    break;
+
+  case 105: // mov_op: "!"
+                { yylhs.value.as < enum mov_op > () = mov_op::invert; }
+    break;
+
+  case 106: // mov_op: "::"
+                { yylhs.value.as < enum mov_op > () = mov_op::bit_reverse; }
+    break;
+
+  case 107: // mov_op: %empty
+                { yylhs.value.as < enum mov_op > () = mov_op::none; }
+    break;
+
+  case 108: // set_target: "pins"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_pins; }
+    break;
+
+  case 109: // set_target: "x"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_x; }
+    break;
+
+  case 110: // set_target: "y"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_y; }
+    break;
+
+  case 111: // set_target: "pindirs"
+                { yylhs.value.as < enum in_out_set > () = in_out_set::in_out_set_pindirs; }
+    break;
+
+  case 112: // if_full: "iffull"
+           { yylhs.value.as < bool > () = true; }
+    break;
+
+  case 113: // if_full: %empty
+           { yylhs.value.as < bool > () = false; }
+    break;
+
+  case 114: // if_empty: "ifempty"
+            { yylhs.value.as < bool > () = true; }
+    break;
+
+  case 115: // if_empty: %empty
+            { yylhs.value.as < bool > () = false; }
+    break;
+
+  case 116: // blocking: "block"
+            { yylhs.value.as < bool > () = true; }
+    break;
+
+  case 117: // blocking: "noblock"
+            { yylhs.value.as < bool > () = false; }
+    break;
+
+  case 118: // blocking: %empty
+            { yylhs.value.as < bool > () = true; }
+    break;
+
+  case 119: // irq_modifiers: "clear"
+                   { yylhs.value.as < enum irq > () = irq::clear; }
+    break;
+
+  case 120: // irq_modifiers: "wait"
+                   { yylhs.value.as < enum irq > () = irq::set_wait; }
+    break;
+
+  case 121: // irq_modifiers: "nowait"
+                   { yylhs.value.as < enum irq > () = irq::set; }
+    break;
+
+  case 122: // irq_modifiers: "set"
+                   { yylhs.value.as < enum irq > () = irq::set; }
+    break;
+
+  case 123: // irq_modifiers: %empty
+                   { yylhs.value.as < enum irq > () = irq::set; }
+    break;
+
+  case 124: // symbol_def: "identifier"
+                    { yylhs.value.as < std::shared_ptr<symbol> > () = std::shared_ptr<symbol>(new symbol(yylhs.location, yystack_[0].value.as < std::string > ())); }
+    break;
+
+  case 125: // symbol_def: "public" "identifier"
+                    { yylhs.value.as < std::shared_ptr<symbol> > () = std::shared_ptr<symbol>(new symbol(yylhs.location, yystack_[0].value.as < std::string > (), true)); }
+    break;
+
+  case 126: // symbol_def: "*" "identifier"
+                    { yylhs.value.as < std::shared_ptr<symbol> > () = std::shared_ptr<symbol>(new symbol(yylhs.location, yystack_[0].value.as < std::string > (), true)); }
+    break;
+
+
+
+            default:
+              break;
+            }
+        }
+#if YY_EXCEPTIONS
+      catch (const syntax_error& yyexc)
+        {
+          YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+          error (yyexc);
+          YYERROR;
+        }
+#endif // YY_EXCEPTIONS
+      YY_SYMBOL_PRINT ("-> $$ =", yylhs);
+      yypop_ (yylen);
+      yylen = 0;
+
+      // Shift the result of the reduction.
+      yypush_ (YY_NULLPTR, YY_MOVE (yylhs));
+    }
+    goto yynewstate;
+
+
+  /*--------------------------------------.
+  | yyerrlab -- here on detecting error.  |
+  `--------------------------------------*/
+  yyerrlab:
+    // If not already recovering from an error, report this error.
+    if (!yyerrstatus_)
+      {
+        ++yynerrs_;
+        context yyctx (*this, yyla);
+        std::string msg = yysyntax_error_ (yyctx);
+        error (yyla.location, YY_MOVE (msg));
+      }
+
+
+    yyerror_range[1].location = yyla.location;
+    if (yyerrstatus_ == 3)
+      {
+        /* If just tried and failed to reuse lookahead token after an
+           error, discard it.  */
+
+        // Return failure if at end of input.
+        if (yyla.kind () == symbol_kind::S_YYEOF)
+          YYABORT;
+        else if (!yyla.empty ())
+          {
+            yy_destroy_ ("Error: discarding", yyla);
+            yyla.clear ();
+          }
+      }
+
+    // Else will try to reuse lookahead token after shifting the error token.
+    goto yyerrlab1;
+
+
+  /*---------------------------------------------------.
+  | yyerrorlab -- error raised explicitly by YYERROR.  |
+  `---------------------------------------------------*/
+  yyerrorlab:
+    /* Pacify compilers when the user code never invokes YYERROR and
+       the label yyerrorlab therefore never appears in user code.  */
+    if (false)
+      YYERROR;
+
+    /* Do not reclaim the symbols of the rule whose action triggered
+       this YYERROR.  */
+    yypop_ (yylen);
+    yylen = 0;
+    YY_STACK_PRINT ();
+    goto yyerrlab1;
+
+
+  /*-------------------------------------------------------------.
+  | yyerrlab1 -- common code for both syntax error and YYERROR.  |
+  `-------------------------------------------------------------*/
+  yyerrlab1:
+    yyerrstatus_ = 3;   // Each real token shifted decrements this.
+    // Pop stack until we find a state that shifts the error token.
+    for (;;)
+      {
+        yyn = yypact_[+yystack_[0].state];
+        if (!yy_pact_value_is_default_ (yyn))
+          {
+            yyn += symbol_kind::S_YYerror;
+            if (0 <= yyn && yyn <= yylast_
+                && yycheck_[yyn] == symbol_kind::S_YYerror)
+              {
+                yyn = yytable_[yyn];
+                if (0 < yyn)
+                  break;
+              }
+          }
+
+        // Pop the current state because it cannot handle the error token.
+        if (yystack_.size () == 1)
+          YYABORT;
+
+        yyerror_range[1].location = yystack_[0].location;
+        yy_destroy_ ("Error: popping", yystack_[0]);
+        yypop_ ();
+        YY_STACK_PRINT ();
+      }
+    {
+      stack_symbol_type error_token;
+
+      yyerror_range[2].location = yyla.location;
+      YYLLOC_DEFAULT (error_token.location, yyerror_range, 2);
+
+      // Shift the error token.
+      yy_lac_discard_ ("error recovery");
+      error_token.state = state_type (yyn);
+      yypush_ ("Shifting", YY_MOVE (error_token));
+    }
+    goto yynewstate;
+
+
+  /*-------------------------------------.
+  | yyacceptlab -- YYACCEPT comes here.  |
+  `-------------------------------------*/
+  yyacceptlab:
+    yyresult = 0;
+    goto yyreturn;
+
+
+  /*-----------------------------------.
+  | yyabortlab -- YYABORT comes here.  |
+  `-----------------------------------*/
+  yyabortlab:
+    yyresult = 1;
+    goto yyreturn;
+
+
+  /*-----------------------------------------------------.
+  | yyreturn -- parsing is finished, return the result.  |
+  `-----------------------------------------------------*/
+  yyreturn:
+    if (!yyla.empty ())
+      yy_destroy_ ("Cleanup: discarding lookahead", yyla);
+
+    /* Do not reclaim the symbols of the rule whose action triggered
+       this YYABORT or YYACCEPT.  */
+    yypop_ (yylen);
+    YY_STACK_PRINT ();
+    while (1 < yystack_.size ())
+      {
+        yy_destroy_ ("Cleanup: popping", yystack_[0]);
+        yypop_ ();
+      }
+
+    return yyresult;
+  }
+#if YY_EXCEPTIONS
+    catch (...)
+      {
+        YYCDEBUG << "Exception caught: cleaning lookahead and stack\n";
+        // Do not try to display the values of the reclaimed symbols,
+        // as their printers might throw an exception.
+        if (!yyla.empty ())
+          yy_destroy_ (YY_NULLPTR, yyla);
+
+        while (1 < yystack_.size ())
+          {
+            yy_destroy_ (YY_NULLPTR, yystack_[0]);
+            yypop_ ();
+          }
+        throw;
+      }
+#endif // YY_EXCEPTIONS
+  }
+
+  void
+  parser::error (const syntax_error& yyexc)
+  {
+    error (yyexc.location, yyexc.what ());
+  }
+
+  /* Return YYSTR after stripping away unnecessary quotes and
+     backslashes, so that it's suitable for yyerror.  The heuristic is
+     that double-quoting is unnecessary unless the string contains an
+     apostrophe, a comma, or backslash (other than backslash-backslash).
+     YYSTR is taken from yytname.  */
+  std::string
+  parser::yytnamerr_ (const char *yystr)
+  {
+    if (*yystr == '"')
+      {
+        std::string yyr;
+        char const *yyp = yystr;
+
+        for (;;)
+          switch (*++yyp)
+            {
+            case '\'':
+            case ',':
+              goto do_not_strip_quotes;
+
+            case '\\':
+              if (*++yyp != '\\')
+                goto do_not_strip_quotes;
+              else
+                goto append;
+
+            append:
+            default:
+              yyr += *yyp;
+              break;
+
+            case '"':
+              return yyr;
+            }
+      do_not_strip_quotes: ;
+      }
+
+    return yystr;
+  }
+
+  std::string
+  parser::symbol_name (symbol_kind_type yysymbol)
+  {
+    return yytnamerr_ (yytname_[yysymbol]);
+  }
+
+
+
+  // parser::context.
+  parser::context::context (const parser& yyparser, const symbol_type& yyla)
+    : yyparser_ (yyparser)
+    , yyla_ (yyla)
+  {}
+
+  int
+  parser::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const
+  {
+    // Actual number of expected tokens
+    int yycount = 0;
+
+#if YYDEBUG
+    // Execute LAC once. We don't care if it is successful, we
+    // only do it for the sake of debugging output.
+    if (!yyparser_.yy_lac_established_)
+      yyparser_.yy_lac_check_ (yyla_.kind ());
+#endif
+
+    for (int yyx = 0; yyx < YYNTOKENS; ++yyx)
+      {
+        symbol_kind_type yysym = YY_CAST (symbol_kind_type, yyx);
+        if (yysym != symbol_kind::S_YYerror
+            && yysym != symbol_kind::S_YYUNDEF
+            && yyparser_.yy_lac_check_ (yysym))
+          {
+            if (!yyarg)
+              ++yycount;
+            else if (yycount == yyargn)
+              return 0;
+            else
+              yyarg[yycount++] = yysym;
+          }
+      }
+    if (yyarg && yycount == 0 && 0 < yyargn)
+      yyarg[0] = symbol_kind::S_YYEMPTY;
+    return yycount;
+  }
+
+
+  bool
+  parser::yy_lac_check_ (symbol_kind_type yytoken) const
+  {
+    // Logically, the yylac_stack's lifetime is confined to this function.
+    // Clear it, to get rid of potential left-overs from previous call.
+    yylac_stack_.clear ();
+    // Reduce until we encounter a shift and thereby accept the token.
+#if YYDEBUG
+    YYCDEBUG << "LAC: checking lookahead " << symbol_name (yytoken) << ':';
+#endif
+    std::ptrdiff_t lac_top = 0;
+    while (true)
+      {
+        state_type top_state = (yylac_stack_.empty ()
+                                ? yystack_[lac_top].state
+                                : yylac_stack_.back ());
+        int yyrule = yypact_[+top_state];
+        if (yy_pact_value_is_default_ (yyrule)
+            || (yyrule += yytoken) < 0 || yylast_ < yyrule
+            || yycheck_[yyrule] != yytoken)
+          {
+            // Use the default action.
+            yyrule = yydefact_[+top_state];
+            if (yyrule == 0)
+              {
+                YYCDEBUG << " Err\n";
+                return false;
+              }
+          }
+        else
+          {
+            // Use the action from yytable.
+            yyrule = yytable_[yyrule];
+            if (yy_table_value_is_error_ (yyrule))
+              {
+                YYCDEBUG << " Err\n";
+                return false;
+              }
+            if (0 < yyrule)
+              {
+                YYCDEBUG << " S" << yyrule << '\n';
+                return true;
+              }
+            yyrule = -yyrule;
+          }
+        // By now we know we have to simulate a reduce.
+        YYCDEBUG << " R" << yyrule - 1;
+        // Pop the corresponding number of values from the stack.
+        {
+          std::ptrdiff_t yylen = yyr2_[yyrule];
+          // First pop from the LAC stack as many tokens as possible.
+          std::ptrdiff_t lac_size = std::ptrdiff_t (yylac_stack_.size ());
+          if (yylen < lac_size)
+            {
+              yylac_stack_.resize (std::size_t (lac_size - yylen));
+              yylen = 0;
+            }
+          else if (lac_size)
+            {
+              yylac_stack_.clear ();
+              yylen -= lac_size;
+            }
+          // Only afterwards look at the main stack.
+          // We simulate popping elements by incrementing lac_top.
+          lac_top += yylen;
+        }
+        // Keep top_state in sync with the updated stack.
+        top_state = (yylac_stack_.empty ()
+                     ? yystack_[lac_top].state
+                     : yylac_stack_.back ());
+        // Push the resulting state of the reduction.
+        state_type state = yy_lr_goto_state_ (top_state, yyr1_[yyrule]);
+        YYCDEBUG << " G" << int (state);
+        yylac_stack_.push_back (state);
+      }
+  }
+
+  // Establish the initial context if no initial context currently exists.
+  bool
+  parser::yy_lac_establish_ (symbol_kind_type yytoken)
+  {
+    /* Establish the initial context for the current lookahead if no initial
+       context is currently established.
+
+       We define a context as a snapshot of the parser stacks.  We define
+       the initial context for a lookahead as the context in which the
+       parser initially examines that lookahead in order to select a
+       syntactic action.  Thus, if the lookahead eventually proves
+       syntactically unacceptable (possibly in a later context reached via a
+       series of reductions), the initial context can be used to determine
+       the exact set of tokens that would be syntactically acceptable in the
+       lookahead's place.  Moreover, it is the context after which any
+       further semantic actions would be erroneous because they would be
+       determined by a syntactically unacceptable token.
+
+       yy_lac_establish_ should be invoked when a reduction is about to be
+       performed in an inconsistent state (which, for the purposes of LAC,
+       includes consistent states that don't know they're consistent because
+       their default reductions have been disabled).
+
+       For parse.lac=full, the implementation of yy_lac_establish_ is as
+       follows.  If no initial context is currently established for the
+       current lookahead, then check if that lookahead can eventually be
+       shifted if syntactic actions continue from the current context.  */
+    if (!yy_lac_established_)
+      {
+#if YYDEBUG
+        YYCDEBUG << "LAC: initial context established for "
+                 << symbol_name (yytoken) << '\n';
+#endif
+        yy_lac_established_ = true;
+        return yy_lac_check_ (yytoken);
+      }
+    return true;
+  }
+
+  // Discard any previous initial lookahead context.
+  void
+  parser::yy_lac_discard_ (const char* evt)
+  {
+   /* Discard any previous initial lookahead context because of Event,
+      which may be a lookahead change or an invalidation of the currently
+      established initial context for the current lookahead.
+
+      The most common example of a lookahead change is a shift.  An example
+      of both cases is syntax error recovery.  That is, a syntax error
+      occurs when the lookahead is syntactically erroneous for the
+      currently established initial context, so error recovery manipulates
+      the parser stacks to try to find a new initial context in which the
+      current lookahead is syntactically acceptable.  If it fails to find
+      such a context, it discards the lookahead.  */
+    if (yy_lac_established_)
+      {
+        YYCDEBUG << "LAC: initial context discarded due to "
+                 << evt << '\n';
+        yy_lac_established_ = false;
+      }
+  }
+
+  int
+  parser::yy_syntax_error_arguments_ (const context& yyctx,
+                                                 symbol_kind_type yyarg[], int yyargn) const
+  {
+    /* There are many possibilities here to consider:
+       - If this state is a consistent state with a default action, then
+         the only way this function was invoked is if the default action
+         is an error action.  In that case, don't check for expected
+         tokens because there are none.
+       - The only way there can be no lookahead present (in yyla) is
+         if this state is a consistent state with a default action.
+         Thus, detecting the absence of a lookahead is sufficient to
+         determine that there is no unexpected or expected token to
+         report.  In that case, just report a simple "syntax error".
+       - Don't assume there isn't a lookahead just because this state is
+         a consistent state with a default action.  There might have
+         been a previous inconsistent state, consistent state with a
+         non-default action, or user semantic action that manipulated
+         yyla.  (However, yyla is currently not documented for users.)
+         In the first two cases, it might appear that the current syntax
+         error should have been detected in the previous state when
+         yy_lac_check was invoked.  However, at that time, there might
+         have been a different syntax error that discarded a different
+         initial context during error recovery, leaving behind the
+         current lookahead.
+    */
+
+    if (!yyctx.lookahead ().empty ())
+      {
+        if (yyarg)
+          yyarg[0] = yyctx.token ();
+        int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1);
+        return yyn + 1;
+      }
+    return 0;
+  }
+
+  // Generate an error message.
+  std::string
+  parser::yysyntax_error_ (const context& yyctx) const
+  {
+    // Its maximum.
+    enum { YYARGS_MAX = 5 };
+    // Arguments of yyformat.
+    symbol_kind_type yyarg[YYARGS_MAX];
+    int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX);
+
+    char const* yyformat = YY_NULLPTR;
+    switch (yycount)
+      {
+#define YYCASE_(N, S)                         \
+        case N:                               \
+          yyformat = S;                       \
+        break
+      default: // Avoid compiler warnings.
+        YYCASE_ (0, YY_("syntax error"));
+        YYCASE_ (1, YY_("syntax error, unexpected %s"));
+        YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s"));
+        YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+        YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+        YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+#undef YYCASE_
+      }
+
+    std::string yyres;
+    // Argument number.
+    std::ptrdiff_t yyi = 0;
+    for (char const* yyp = yyformat; *yyp; ++yyp)
+      if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount)
+        {
+          yyres += symbol_name (yyarg[yyi++]);
+          ++yyp;
+        }
+      else
+        yyres += *yyp;
+    return yyres;
+  }
+
+
+  const signed char parser::yypact_ninf_ = -52;
+
+  const signed char parser::yytable_ninf_ = -12;
+
+  const short
+  parser::yypact_[] =
+  {
+       3,   -52,   -41,   -39,   -52,   -52,    -3,     5,     5,     5,
+       7,    44,    10,     0,   101,    18,    30,    94,    51,    50,
+     -52,    20,   -52,    13,   -52,    88,    17,   -52,   -52,   129,
+     -52,   -52,     2,    85,   -52,   -52,     1,     1,   -52,   -52,
+      40,   -52,   -52,   -52,    42,    58,   -52,    28,    96,   120,
+     120,   120,   120,    15,   -52,   -52,   -52,   -52,   -52,   -52,
+     -52,   -52,   120,   -52,   -52,   -52,   -52,   -52,   -52,   -52,
+     -52,   120,   -52,    63,   -52,    63,   -52,   -52,   -52,   -52,
+     -52,   -52,   -52,   120,   -52,   -52,   -52,   -52,     5,   -52,
+     -52,   -52,   -52,   120,   -52,   -52,   -52,   -52,     3,   -52,
+       1,     5,    45,   130,   -52,     1,     1,   -52,   177,   162,
+     -52,    97,   132,   -52,   -52,   -52,   -52,    87,   -52,   -52,
+       1,     5,     5,     5,     5,   -52,     5,     5,   -52,   -52,
+     -52,   -52,    29,   118,     5,   -52,   170,   -52,   -52,   -52,
+     182,   177,     1,     1,     1,     1,     1,     1,     1,   -52,
+     -52,   -51,   -52,   177,   119,   -52,   -52,   -52,   -52,   -52,
+     -52,   -52,    82,   -52,   -52,   -52,   182,   182,   107,   107,
+     -52,   -52,   -52,   -52,   -52,   -52,   -52,   -52,   -52,   -52,
+     -52,   -52,   -52,   -52,   -52
+  };
+
+  const signed char
+  parser::yydefact_[] =
+  {
+       0,    12,     0,     0,    21,    22,     0,     0,     0,     0,
+       0,    69,     0,     0,     0,   113,   115,     0,   123,     0,
+      47,     0,   124,     0,    28,     0,     0,     3,    10,     9,
+       6,     7,    46,     0,   126,     5,     0,     0,    30,    29,
+      20,    23,    16,    27,     0,     0,    67,     0,     0,    75,
+      75,    75,    75,     0,    51,    76,    79,    77,    78,    80,
+      81,    82,    75,    83,    86,    87,    84,    85,    90,    89,
+      88,    75,   112,   118,   114,   118,    91,    92,    93,    94,
+      95,    96,    97,    75,   120,   122,   121,   119,     0,   108,
+     111,   109,   110,    75,   125,    13,     1,     2,     0,     8,
+       0,     0,    45,    44,    14,     0,     0,    32,    15,     0,
+      19,    18,     0,    68,    62,    64,    63,     0,    65,    74,
+       0,     0,     0,     0,     0,    49,     0,     0,   116,   117,
+      54,    55,   107,    58,     0,     4,     0,    61,    43,    42,
+      40,    41,     0,     0,     0,     0,     0,     0,     0,    31,
+      17,     0,    66,    48,    71,    73,    72,    50,    52,    53,
+     105,   106,     0,    57,    59,    60,    33,    34,    35,    36,
+      37,    38,    39,    25,    26,    24,    70,    98,   101,    99,
+     100,   103,   104,   102,    56
+  };
+
+  const short
+  parser::yypgoto_[] =
+  {
+     -52,   -52,   -52,   102,   -52,   -52,   -52,    -7,   -14,   172,
+     -52,    99,   103,   -52,   146,    25,   -52,   -52,   -52,   -52,
+     -52,   -52,   -52,   -52,   128,   -52,   198
+  };
+
+  const short
+  parser::yydefgoto_[] =
+  {
+      -1,    25,    26,    27,    28,    29,    30,   107,   108,    31,
+      32,   102,   103,    49,    54,   120,    62,    71,    83,   184,
+     162,    93,    73,    75,   130,    88,    33
+  };
+
+  const short
+  parser::yytable_[] =
+  {
+      40,    41,    42,   -11,     1,    53,   -11,    37,    43,     2,
+     100,    37,   105,   173,   174,     2,    37,    97,   175,   124,
+      98,   106,    34,   109,    35,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    12,    13,    14,    15,    16,    17,
+      18,    19,    20,    55,    56,   116,   117,    50,   160,   161,
+      51,    52,    50,    57,    58,    51,    52,    59,    60,    21,
+      22,    61,   101,    45,    38,    21,    22,    72,    38,    23,
+      39,    24,    44,    38,    39,   121,   122,   123,    74,    39,
+      95,   133,    84,    94,    46,   110,   136,   126,    96,    85,
+     104,   140,   141,    89,   137,    90,   127,    47,    48,   111,
+     113,    86,    87,    91,    92,   101,   153,   112,   132,   128,
+     129,   114,   115,   118,   154,   155,   156,   157,   134,   158,
+     159,   146,   147,   148,   119,   177,   178,   164,   166,   167,
+     168,   169,   170,   171,   172,   179,   180,    76,   100,   181,
+     182,   152,   150,   183,    63,    64,    65,    77,    78,    79,
+      80,    81,    82,   151,    66,    67,    68,    69,    70,    11,
+      12,    13,    14,    15,    16,    17,    18,    19,    20,   149,
+     163,   176,   142,   143,   144,   145,   146,   147,   148,   165,
+     142,   143,   144,   145,   146,   147,   148,   142,   143,   144,
+     145,   146,   147,   148,   144,   145,   146,   147,   148,   125,
+     135,    99,   139,   131,    36,   138
+  };
+
+  const unsigned char
+  parser::yycheck_[] =
+  {
+       7,     8,     9,     0,     1,    12,     3,     6,     1,    12,
+       8,     6,    11,    64,    65,    12,     6,     0,    69,     4,
+       3,    20,    63,    37,    63,    22,    23,    24,    25,    26,
+      27,    28,    29,    30,    31,    32,    33,    34,    35,    36,
+      37,    38,    39,    43,    44,    17,    18,    37,    19,    20,
+      40,    41,    37,    53,    54,    40,    41,    57,    58,    62,
+      63,    61,    60,    19,    63,    62,    63,    49,    63,    66,
+      69,    68,    65,    63,    69,    50,    51,    52,    48,    69,
+      67,    88,    31,    63,    40,    45,   100,    62,     0,    38,
+       5,   105,   106,    43,   101,    45,    71,    53,    54,    59,
+      42,    50,    51,    53,    54,    60,   120,    65,    83,    46,
+      47,    53,    54,    17,   121,   122,   123,   124,    93,   126,
+     127,    14,    15,    16,     4,    43,    44,   134,   142,   143,
+     144,   145,   146,   147,   148,    53,    54,    43,     8,    57,
+      58,    54,    45,    61,    43,    44,    45,    53,    54,    55,
+      56,    57,    58,    21,    53,    54,    55,    56,    57,    30,
+      31,    32,    33,    34,    35,    36,    37,    38,    39,     7,
+      52,    52,    10,    11,    12,    13,    14,    15,    16,     9,
+      10,    11,    12,    13,    14,    15,    16,    10,    11,    12,
+      13,    14,    15,    16,    12,    13,    14,    15,    16,    53,
+      98,    29,   103,    75,     6,   102
+  };
+
+  const signed char
+  parser::yystos_[] =
+  {
+       0,     1,    12,    22,    23,    24,    25,    26,    27,    28,
+      29,    30,    31,    32,    33,    34,    35,    36,    37,    38,
+      39,    62,    63,    66,    68,    71,    72,    73,    74,    75,
+      76,    79,    80,    96,    63,    63,    96,     6,    63,    69,
+      77,    77,    77,     1,    65,    19,    40,    53,    54,    83,
+      37,    40,    41,    77,    84,    43,    44,    53,    54,    57,
+      58,    61,    86,    43,    44,    45,    53,    54,    55,    56,
+      57,    87,    49,    92,    48,    93,    43,    53,    54,    55,
+      56,    57,    58,    88,    31,    38,    50,    51,    95,    43,
+      45,    53,    54,    91,    63,    67,     0,     0,     3,    79,
+       8,    60,    81,    82,     5,    11,    20,    77,    78,    78,
+      45,    59,    65,    42,    53,    54,    17,    18,    17,     4,
+      85,    85,    85,    85,     4,    84,    85,    85,    46,    47,
+      94,    94,    85,    77,    85,    73,    78,    77,    82,    81,
+      78,    78,    10,    11,    12,    13,    14,    15,    16,     7,
+      45,    21,    54,    78,    77,    77,    77,    77,    77,    77,
+      19,    20,    90,    52,    77,     9,    78,    78,    78,    78,
+      78,    78,    78,    64,    65,    69,    52,    43,    44,    53,
+      54,    57,    58,    61,    89
+  };
+
+  const signed char
+  parser::yyr1_[] =
+  {
+       0,    70,    71,    72,    72,    73,    73,    73,    73,    73,
+      73,    73,    73,    74,    75,    76,    76,    76,    76,    76,
+      76,    76,    76,    76,    76,    76,    76,    76,    76,    77,
+      77,    77,    78,    78,    78,    78,    78,    78,    78,    78,
+      78,    78,    79,    79,    79,    79,    79,    80,    80,    80,
+      80,    80,    80,    80,    80,    80,    80,    80,    80,    80,
+      81,    82,    83,    83,    83,    83,    83,    83,    83,    83,
+      84,    84,    84,    84,    85,    85,    86,    86,    86,    86,
+      86,    86,    86,    87,    87,    87,    87,    87,    87,    87,
+      87,    88,    88,    88,    88,    88,    88,    88,    89,    89,
+      89,    89,    89,    89,    89,    90,    90,    90,    91,    91,
+      91,    91,    92,    92,    93,    93,    94,    94,    94,    95,
+      95,    95,    95,    95,    96,    96,    96
+  };
+
+  const signed char
+  parser::yyr2_[] =
+  {
+       0,     2,     2,     1,     3,     2,     1,     1,     2,     1,
+       1,     0,     1,     2,     2,     3,     2,     4,     3,     3,
+       2,     1,     1,     2,     5,     5,     5,     2,     1,     1,
+       1,     3,     1,     3,     3,     3,     3,     3,     3,     3,
+       2,     2,     3,     3,     2,     2,     1,     1,     4,     3,
+       4,     2,     4,     4,     3,     3,     5,     4,     3,     4,
+       3,     2,     2,     2,     2,     2,     3,     1,     2,     0,
+       4,     3,     3,     3,     1,     0,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     0,     1,     1,
+       1,     1,     1,     0,     1,     0,     1,     1,     0,     1,
+       1,     1,     1,     0,     1,     2,     2
+  };
+
+
+#if YYDEBUG || 1
+  // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+  // First, the terminals, then, starting at \a YYNTOKENS, nonterminals.
+  const char*
+  const parser::yytname_[] =
+  {
+  "\"end of file\"", "error", "\"invalid token\"", "\"end of line\"",
+  "\",\"", "\":\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"+\"", "\"-\"",
+  "\"*\"", "\"/\"", "\"|\"", "\"&\"", "\"^\"", "\"--\"", "\"!=\"", "\"!\"",
+  "\"::\"", "\"=\"", "\".program\"", "\".wrap_target\"", "\".wrap\"",
+  "\".define\"", "\".side_set\"", "\".word\"", "\".origin\"",
+  "\".lang_opt\"", "\"jmp\"", "\"wait\"", "\"in\"", "\"out\"", "\"push\"",
+  "\"pull\"", "\"mov\"", "\"irq\"", "\"set\"", "\"nop\"", "\"pin\"",
+  "\"gpio\"", "\"osre\"", "\"pins\"", "\"null\"", "\"pindirs\"",
+  "\"block\"", "\"noblock\"", "\"ifempty\"", "\"iffull\"", "\"nowait\"",
+  "\"clear\"", "\"rel\"", "\"x\"", "\"y\"", "\"exec\"", "\"pc\"",
+  "\"isr\"", "\"osr\"", "\"opt\"", "\"side\"", "\"status\"", "\"public\"",
+  "\"identifier\"", "\"string\"", "\"text\"", "\"code block\"", "\"%}\"",
+  "UNKNOWN_DIRECTIVE", "\"integer\"", "$accept", "file", "lines", "line",
+  "code_block", "label_decl", "directive", "value", "expression",
+  "instruction", "base_instruction", "delay", "sideset", "condition",
+  "wait_source", "comma", "in_source", "out_target", "mov_target",
+  "mov_source", "mov_op", "set_target", "if_full", "if_empty", "blocking",
+  "irq_modifiers", "symbol_def", YY_NULLPTR
+  };
+#endif
+
+
+#if YYDEBUG
+  const short
+  parser::yyrline_[] =
+  {
+       0,   136,   136,   140,   141,   144,   145,   146,   147,   148,
+     149,   150,   151,   155,   159,   162,   163,   164,   165,   166,
+     167,   168,   169,   170,   171,   172,   173,   174,   175,   180,
+     181,   182,   186,   187,   188,   189,   190,   191,   192,   193,
+     194,   195,   199,   200,   201,   202,   203,   207,   208,   209,
+     210,   211,   212,   213,   214,   215,   216,   217,   218,   219,
+     224,   228,   232,   233,   234,   235,   236,   237,   238,   239,
+     243,   244,   245,   246,   248,   248,   251,   252,   253,   254,
+     255,   256,   257,   260,   261,   262,   263,   264,   265,   266,
+     267,   270,   271,   272,   273,   274,   275,   276,   279,   280,
+     281,   282,   283,   284,   285,   289,   290,   291,   295,   296,
+     297,   298,   302,   303,   307,   308,   312,   313,   314,   318,
+     319,   320,   321,   322,   326,   327,   328
+  };
+
+  void
+  parser::yy_stack_print_ () const
+  {
+    *yycdebug_ << "Stack now";
+    for (stack_type::const_iterator
+           i = yystack_.begin (),
+           i_end = yystack_.end ();
+         i != i_end; ++i)
+      *yycdebug_ << ' ' << int (i->state);
+    *yycdebug_ << '\n';
+  }
+
+  void
+  parser::yy_reduce_print_ (int yyrule) const
+  {
+    int yylno = yyrline_[yyrule];
+    int yynrhs = yyr2_[yyrule];
+    // Print the symbols being reduced, and their result.
+    *yycdebug_ << "Reducing stack by rule " << yyrule - 1
+               << " (line " << yylno << "):\n";
+    // The symbols being reduced.
+    for (int yyi = 0; yyi < yynrhs; yyi++)
+      YY_SYMBOL_PRINT ("   $" << yyi + 1 << " =",
+                       yystack_[(yynrhs) - (yyi + 1)]);
+  }
+#endif // YYDEBUG
+
+
+} // yy
+
+
+void yy::parser::error(const location_type& l, const std::string& m)
+{
+   if (l.begin.filename) {
+      std::cerr << l << ": " << m << '\n';
+      pioasm.error_count++;
+      if (l.begin.line == l.end.line && *l.begin.filename == *l.end.filename) {
+        std::ifstream file(l.begin.filename->c_str());
+        std::string line;
+        for(int i = 0; i < l.begin.line; ++i) {
+             std::getline(file, line);
+        }
+        fprintf(stderr, "%5d | %s\n", l.begin.line, line.c_str());
+        fprintf(stderr, "%5s | %*s", "", l.begin.column, "^");
+        for (int i = l.begin.column; i < l.end.column - 1; i++) {
+              putc ('~', stderr);
+        }
+        putc ('\n', stderr);
+      }
+  } else {
+      std::cerr << m << '\n';
+  }
+}
+
diff --git a/tools/pioasm/gen/parser.hpp b/tools/pioasm/gen/parser.hpp
new file mode 100644
index 0000000..11d8393
--- /dev/null
+++ b/tools/pioasm/gen/parser.hpp
@@ -0,0 +1,2894 @@
+// A Bison parser, made by GNU Bison 3.7.2.
+
+// Skeleton interface for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton.  Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+
+/**
+ ** \file pico_sdk/tools/pioasm/gen/parser.hpp
+ ** Define the yy::parser class.
+ */
+
+// C++ LALR(1) parser skeleton written by Akim Demaille.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_.  They are
+// private implementation details that can be changed or removed.
+
+#ifndef YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_PARSER_HPP_INCLUDED
+# define YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_PARSER_HPP_INCLUDED
+// "%code requires" blocks.
+
+  #include <string>
+  #include <fstream>
+  #include <sstream>
+  #include "pio_types.h"
+  struct pio_assembler;
+
+  #ifdef _MSC_VER
+  #pragma warning(disable : 4065) // default only switch statement
+  #endif
+
+
+
+# include <cstdlib> // std::abort
+# include <iostream>
+# include <stdexcept>
+# include <string>
+# include <vector>
+
+#if defined __cplusplus
+# define YY_CPLUSPLUS __cplusplus
+#else
+# define YY_CPLUSPLUS 199711L
+#endif
+
+// Support move semantics when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_MOVE           std::move
+# define YY_MOVE_OR_COPY   move
+# define YY_MOVE_REF(Type) Type&&
+# define YY_RVREF(Type)    Type&&
+# define YY_COPY(Type)     Type
+#else
+# define YY_MOVE
+# define YY_MOVE_OR_COPY   copy
+# define YY_MOVE_REF(Type) Type&
+# define YY_RVREF(Type)    const Type&
+# define YY_COPY(Type)     const Type&
+#endif
+
+// Support noexcept when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_NOEXCEPT noexcept
+# define YY_NOTHROW
+#else
+# define YY_NOEXCEPT
+# define YY_NOTHROW throw ()
+#endif
+
+// Support constexpr when possible.
+#if 201703 <= YY_CPLUSPLUS
+# define YY_CONSTEXPR constexpr
+#else
+# define YY_CONSTEXPR
+#endif
+# include "location.h"
+
+#ifndef YY_ASSERT
+# include <cassert>
+# define YY_ASSERT assert
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+#  define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+#  define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                            \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+#  else
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+#  endif
+# endif
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
+
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+namespace yy {
+
+
+
+
+  /// A Bison parser.
+  class parser
+  {
+  public:
+#ifndef YYSTYPE
+  /// A buffer to store and retrieve objects.
+  ///
+  /// Sort of a variant, but does not keep track of the nature
+  /// of the stored data, since that knowledge is available
+  /// via the current parser state.
+  class semantic_type
+  {
+  public:
+    /// Type of *this.
+    typedef semantic_type self_type;
+
+    /// Empty construction.
+    semantic_type () YY_NOEXCEPT
+      : yybuffer_ ()
+    {}
+
+    /// Construct and fill.
+    template <typename T>
+    semantic_type (YY_RVREF (T) t)
+    {
+      YY_ASSERT (sizeof (T) <= size);
+      new (yyas_<T> ()) T (YY_MOVE (t));
+    }
+
+#if 201103L <= YY_CPLUSPLUS
+    /// Non copyable.
+    semantic_type (const self_type&) = delete;
+    /// Non copyable.
+    self_type& operator= (const self_type&) = delete;
+#endif
+
+    /// Destruction, allowed only if empty.
+    ~semantic_type () YY_NOEXCEPT
+    {}
+
+# if 201103L <= YY_CPLUSPLUS
+    /// Instantiate a \a T in here from \a t.
+    template <typename T, typename... U>
+    T&
+    emplace (U&&... u)
+    {
+      return *new (yyas_<T> ()) T (std::forward <U>(u)...);
+    }
+# else
+    /// Instantiate an empty \a T in here.
+    template <typename T>
+    T&
+    emplace ()
+    {
+      return *new (yyas_<T> ()) T ();
+    }
+
+    /// Instantiate a \a T in here from \a t.
+    template <typename T>
+    T&
+    emplace (const T& t)
+    {
+      return *new (yyas_<T> ()) T (t);
+    }
+# endif
+
+    /// Instantiate an empty \a T in here.
+    /// Obsolete, use emplace.
+    template <typename T>
+    T&
+    build ()
+    {
+      return emplace<T> ();
+    }
+
+    /// Instantiate a \a T in here from \a t.
+    /// Obsolete, use emplace.
+    template <typename T>
+    T&
+    build (const T& t)
+    {
+      return emplace<T> (t);
+    }
+
+    /// Accessor to a built \a T.
+    template <typename T>
+    T&
+    as () YY_NOEXCEPT
+    {
+      return *yyas_<T> ();
+    }
+
+    /// Const accessor to a built \a T (for %printer).
+    template <typename T>
+    const T&
+    as () const YY_NOEXCEPT
+    {
+      return *yyas_<T> ();
+    }
+
+    /// Swap the content with \a that, of same type.
+    ///
+    /// Both variants must be built beforehand, because swapping the actual
+    /// data requires reading it (with as()), and this is not possible on
+    /// unconstructed variants: it would require some dynamic testing, which
+    /// should not be the variant's responsibility.
+    /// Swapping between built and (possibly) non-built is done with
+    /// self_type::move ().
+    template <typename T>
+    void
+    swap (self_type& that) YY_NOEXCEPT
+    {
+      std::swap (as<T> (), that.as<T> ());
+    }
+
+    /// Move the content of \a that to this.
+    ///
+    /// Destroys \a that.
+    template <typename T>
+    void
+    move (self_type& that)
+    {
+# if 201103L <= YY_CPLUSPLUS
+      emplace<T> (std::move (that.as<T> ()));
+# else
+      emplace<T> ();
+      swap<T> (that);
+# endif
+      that.destroy<T> ();
+    }
+
+# if 201103L <= YY_CPLUSPLUS
+    /// Move the content of \a that to this.
+    template <typename T>
+    void
+    move (self_type&& that)
+    {
+      emplace<T> (std::move (that.as<T> ()));
+      that.destroy<T> ();
+    }
+#endif
+
+    /// Copy the content of \a that to this.
+    template <typename T>
+    void
+    copy (const self_type& that)
+    {
+      emplace<T> (that.as<T> ());
+    }
+
+    /// Destroy the stored \a T.
+    template <typename T>
+    void
+    destroy ()
+    {
+      as<T> ().~T ();
+    }
+
+  private:
+#if YY_CPLUSPLUS < 201103L
+    /// Non copyable.
+    semantic_type (const self_type&);
+    /// Non copyable.
+    self_type& operator= (const self_type&);
+#endif
+
+    /// Accessor to raw memory as \a T.
+    template <typename T>
+    T*
+    yyas_ () YY_NOEXCEPT
+    {
+      void *yyp = yybuffer_.yyraw;
+      return static_cast<T*> (yyp);
+     }
+
+    /// Const accessor to raw memory as \a T.
+    template <typename T>
+    const T*
+    yyas_ () const YY_NOEXCEPT
+    {
+      const void *yyp = yybuffer_.yyraw;
+      return static_cast<const T*> (yyp);
+     }
+
+    /// An auxiliary type to compute the largest semantic type.
+    union union_type
+    {
+      // if_full
+      // if_empty
+      // blocking
+      char dummy1[sizeof (bool)];
+
+      // condition
+      char dummy2[sizeof (enum condition)];
+
+      // in_source
+      // out_target
+      // set_target
+      char dummy3[sizeof (enum in_out_set)];
+
+      // irq_modifiers
+      char dummy4[sizeof (enum irq)];
+
+      // mov_target
+      // mov_source
+      char dummy5[sizeof (enum mov)];
+
+      // mov_op
+      char dummy6[sizeof (enum mov_op)];
+
+      // "integer"
+      char dummy7[sizeof (int)];
+
+      // instruction
+      // base_instruction
+      char dummy8[sizeof (std::shared_ptr<instruction>)];
+
+      // value
+      // expression
+      // delay
+      // sideset
+      char dummy9[sizeof (std::shared_ptr<resolvable>)];
+
+      // label_decl
+      // symbol_def
+      char dummy10[sizeof (std::shared_ptr<symbol>)];
+
+      // wait_source
+      char dummy11[sizeof (std::shared_ptr<wait_source>)];
+
+      // "identifier"
+      // "string"
+      // "text"
+      // "code block"
+      // "%}"
+      // UNKNOWN_DIRECTIVE
+      char dummy12[sizeof (std::string)];
+    };
+
+    /// The size of the largest semantic type.
+    enum { size = sizeof (union_type) };
+
+    /// A buffer to store semantic values.
+    union
+    {
+      /// Strongest alignment constraints.
+      long double yyalign_me;
+      /// A buffer large enough to store any of the semantic values.
+      char yyraw[size];
+    } yybuffer_;
+  };
+
+#else
+    typedef YYSTYPE semantic_type;
+#endif
+    /// Symbol locations.
+    typedef location location_type;
+
+    /// Syntax errors thrown from user actions.
+    struct syntax_error : std::runtime_error
+    {
+      syntax_error (const location_type& l, const std::string& m)
+        : std::runtime_error (m)
+        , location (l)
+      {}
+
+      syntax_error (const syntax_error& s)
+        : std::runtime_error (s.what ())
+        , location (s.location)
+      {}
+
+      ~syntax_error () YY_NOEXCEPT YY_NOTHROW;
+
+      location_type location;
+    };
+
+    /// Token kinds.
+    struct token
+    {
+      enum token_kind_type
+      {
+        TOK_YYEMPTY = -2,
+    TOK_END = 0,                   // "end of file"
+    TOK_YYerror = 256,             // error
+    TOK_YYUNDEF = 257,             // "invalid token"
+    TOK_NEWLINE = 258,             // "end of line"
+    TOK_COMMA = 259,               // ","
+    TOK_COLON = 260,               // ":"
+    TOK_LPAREN = 261,              // "("
+    TOK_RPAREN = 262,              // ")"
+    TOK_LBRACKET = 263,            // "["
+    TOK_RBRACKET = 264,            // "]"
+    TOK_PLUS = 265,                // "+"
+    TOK_MINUS = 266,               // "-"
+    TOK_MULTIPLY = 267,            // "*"
+    TOK_DIVIDE = 268,              // "/"
+    TOK_OR = 269,                  // "|"
+    TOK_AND = 270,                 // "&"
+    TOK_XOR = 271,                 // "^"
+    TOK_POST_DECREMENT = 272,      // "--"
+    TOK_NOT_EQUAL = 273,           // "!="
+    TOK_NOT = 274,                 // "!"
+    TOK_REVERSE = 275,             // "::"
+    TOK_EQUAL = 276,               // "="
+    TOK_PROGRAM = 277,             // ".program"
+    TOK_WRAP_TARGET = 278,         // ".wrap_target"
+    TOK_WRAP = 279,                // ".wrap"
+    TOK_DEFINE = 280,              // ".define"
+    TOK_SIDE_SET = 281,            // ".side_set"
+    TOK_WORD = 282,                // ".word"
+    TOK_ORIGIN = 283,              // ".origin"
+    TOK_LANG_OPT = 284,            // ".lang_opt"
+    TOK_JMP = 285,                 // "jmp"
+    TOK_WAIT = 286,                // "wait"
+    TOK_IN = 287,                  // "in"
+    TOK_OUT = 288,                 // "out"
+    TOK_PUSH = 289,                // "push"
+    TOK_PULL = 290,                // "pull"
+    TOK_MOV = 291,                 // "mov"
+    TOK_IRQ = 292,                 // "irq"
+    TOK_SET = 293,                 // "set"
+    TOK_NOP = 294,                 // "nop"
+    TOK_PIN = 295,                 // "pin"
+    TOK_GPIO = 296,                // "gpio"
+    TOK_OSRE = 297,                // "osre"
+    TOK_PINS = 298,                // "pins"
+    TOK_NULL = 299,                // "null"
+    TOK_PINDIRS = 300,             // "pindirs"
+    TOK_BLOCK = 301,               // "block"
+    TOK_NOBLOCK = 302,             // "noblock"
+    TOK_IFEMPTY = 303,             // "ifempty"
+    TOK_IFFULL = 304,              // "iffull"
+    TOK_NOWAIT = 305,              // "nowait"
+    TOK_CLEAR = 306,               // "clear"
+    TOK_REL = 307,                 // "rel"
+    TOK_X = 308,                   // "x"
+    TOK_Y = 309,                   // "y"
+    TOK_EXEC = 310,                // "exec"
+    TOK_PC = 311,                  // "pc"
+    TOK_ISR = 312,                 // "isr"
+    TOK_OSR = 313,                 // "osr"
+    TOK_OPTIONAL = 314,            // "opt"
+    TOK_SIDE = 315,                // "side"
+    TOK_STATUS = 316,              // "status"
+    TOK_PUBLIC = 317,              // "public"
+    TOK_ID = 318,                  // "identifier"
+    TOK_STRING = 319,              // "string"
+    TOK_NON_WS = 320,              // "text"
+    TOK_CODE_BLOCK_START = 321,    // "code block"
+    TOK_CODE_BLOCK_CONTENTS = 322, // "%}"
+    TOK_UNKNOWN_DIRECTIVE = 323,   // UNKNOWN_DIRECTIVE
+    TOK_INT = 324                  // "integer"
+      };
+      /// Backward compatibility alias (Bison 3.6).
+      typedef token_kind_type yytokentype;
+    };
+
+    /// Token kind, as returned by yylex.
+    typedef token::yytokentype token_kind_type;
+
+    /// Backward compatibility alias (Bison 3.6).
+    typedef token_kind_type token_type;
+
+    /// Symbol kinds.
+    struct symbol_kind
+    {
+      enum symbol_kind_type
+      {
+        YYNTOKENS = 70, ///< Number of tokens.
+        S_YYEMPTY = -2,
+        S_YYEOF = 0,                             // "end of file"
+        S_YYerror = 1,                           // error
+        S_YYUNDEF = 2,                           // "invalid token"
+        S_NEWLINE = 3,                           // "end of line"
+        S_COMMA = 4,                             // ","
+        S_COLON = 5,                             // ":"
+        S_LPAREN = 6,                            // "("
+        S_RPAREN = 7,                            // ")"
+        S_LBRACKET = 8,                          // "["
+        S_RBRACKET = 9,                          // "]"
+        S_PLUS = 10,                             // "+"
+        S_MINUS = 11,                            // "-"
+        S_MULTIPLY = 12,                         // "*"
+        S_DIVIDE = 13,                           // "/"
+        S_OR = 14,                               // "|"
+        S_AND = 15,                              // "&"
+        S_XOR = 16,                              // "^"
+        S_POST_DECREMENT = 17,                   // "--"
+        S_NOT_EQUAL = 18,                        // "!="
+        S_NOT = 19,                              // "!"
+        S_REVERSE = 20,                          // "::"
+        S_EQUAL = 21,                            // "="
+        S_PROGRAM = 22,                          // ".program"
+        S_WRAP_TARGET = 23,                      // ".wrap_target"
+        S_WRAP = 24,                             // ".wrap"
+        S_DEFINE = 25,                           // ".define"
+        S_SIDE_SET = 26,                         // ".side_set"
+        S_WORD = 27,                             // ".word"
+        S_ORIGIN = 28,                           // ".origin"
+        S_LANG_OPT = 29,                         // ".lang_opt"
+        S_JMP = 30,                              // "jmp"
+        S_WAIT = 31,                             // "wait"
+        S_IN = 32,                               // "in"
+        S_OUT = 33,                              // "out"
+        S_PUSH = 34,                             // "push"
+        S_PULL = 35,                             // "pull"
+        S_MOV = 36,                              // "mov"
+        S_IRQ = 37,                              // "irq"
+        S_SET = 38,                              // "set"
+        S_NOP = 39,                              // "nop"
+        S_PIN = 40,                              // "pin"
+        S_GPIO = 41,                             // "gpio"
+        S_OSRE = 42,                             // "osre"
+        S_PINS = 43,                             // "pins"
+        S_NULL = 44,                             // "null"
+        S_PINDIRS = 45,                          // "pindirs"
+        S_BLOCK = 46,                            // "block"
+        S_NOBLOCK = 47,                          // "noblock"
+        S_IFEMPTY = 48,                          // "ifempty"
+        S_IFFULL = 49,                           // "iffull"
+        S_NOWAIT = 50,                           // "nowait"
+        S_CLEAR = 51,                            // "clear"
+        S_REL = 52,                              // "rel"
+        S_X = 53,                                // "x"
+        S_Y = 54,                                // "y"
+        S_EXEC = 55,                             // "exec"
+        S_PC = 56,                               // "pc"
+        S_ISR = 57,                              // "isr"
+        S_OSR = 58,                              // "osr"
+        S_OPTIONAL = 59,                         // "opt"
+        S_SIDE = 60,                             // "side"
+        S_STATUS = 61,                           // "status"
+        S_PUBLIC = 62,                           // "public"
+        S_ID = 63,                               // "identifier"
+        S_STRING = 64,                           // "string"
+        S_NON_WS = 65,                           // "text"
+        S_CODE_BLOCK_START = 66,                 // "code block"
+        S_CODE_BLOCK_CONTENTS = 67,              // "%}"
+        S_UNKNOWN_DIRECTIVE = 68,                // UNKNOWN_DIRECTIVE
+        S_INT = 69,                              // "integer"
+        S_YYACCEPT = 70,                         // $accept
+        S_file = 71,                             // file
+        S_lines = 72,                            // lines
+        S_line = 73,                             // line
+        S_code_block = 74,                       // code_block
+        S_label_decl = 75,                       // label_decl
+        S_directive = 76,                        // directive
+        S_value = 77,                            // value
+        S_expression = 78,                       // expression
+        S_instruction = 79,                      // instruction
+        S_base_instruction = 80,                 // base_instruction
+        S_delay = 81,                            // delay
+        S_sideset = 82,                          // sideset
+        S_condition = 83,                        // condition
+        S_wait_source = 84,                      // wait_source
+        S_comma = 85,                            // comma
+        S_in_source = 86,                        // in_source
+        S_out_target = 87,                       // out_target
+        S_mov_target = 88,                       // mov_target
+        S_mov_source = 89,                       // mov_source
+        S_mov_op = 90,                           // mov_op
+        S_set_target = 91,                       // set_target
+        S_if_full = 92,                          // if_full
+        S_if_empty = 93,                         // if_empty
+        S_blocking = 94,                         // blocking
+        S_irq_modifiers = 95,                    // irq_modifiers
+        S_symbol_def = 96                        // symbol_def
+      };
+    };
+
+    /// (Internal) symbol kind.
+    typedef symbol_kind::symbol_kind_type symbol_kind_type;
+
+    /// The number of tokens.
+    static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS;
+
+    /// A complete symbol.
+    ///
+    /// Expects its Base type to provide access to the symbol kind
+    /// via kind ().
+    ///
+    /// Provide access to semantic value and location.
+    template <typename Base>
+    struct basic_symbol : Base
+    {
+      /// Alias to Base.
+      typedef Base super_type;
+
+      /// Default constructor.
+      basic_symbol ()
+        : value ()
+        , location ()
+      {}
+
+#if 201103L <= YY_CPLUSPLUS
+      /// Move constructor.
+      basic_symbol (basic_symbol&& that)
+        : Base (std::move (that))
+        , value ()
+        , location (std::move (that.location))
+      {
+        switch (this->kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.move< bool > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.move< enum condition > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.move< enum in_out_set > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.move< enum irq > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.move< enum mov > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.move< enum mov_op > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.move< int > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.move< std::shared_ptr<instruction> > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.move< std::shared_ptr<resolvable> > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.move< std::shared_ptr<symbol> > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.move< std::shared_ptr<wait_source> > (std::move (that.value));
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.move< std::string > (std::move (that.value));
+        break;
+
+      default:
+        break;
+    }
+
+      }
+#endif
+
+      /// Copy constructor.
+      basic_symbol (const basic_symbol& that);
+
+      /// Constructor for valueless symbols, and symbols from each type.
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, location_type&& l)
+        : Base (t)
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const location_type& l)
+        : Base (t)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, bool&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const bool& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, enum condition&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const enum condition& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, enum in_out_set&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const enum in_out_set& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, enum irq&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const enum irq& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, enum mov&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const enum mov& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, enum mov_op&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const enum mov_op& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, int&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const int& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, std::shared_ptr<instruction>&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const std::shared_ptr<instruction>& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, std::shared_ptr<resolvable>&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const std::shared_ptr<resolvable>& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, std::shared_ptr<symbol>&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const std::shared_ptr<symbol>& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, std::shared_ptr<wait_source>&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const std::shared_ptr<wait_source>& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      basic_symbol (typename Base::kind_type t, std::string&& v, location_type&& l)
+        : Base (t)
+        , value (std::move (v))
+        , location (std::move (l))
+      {}
+#else
+      basic_symbol (typename Base::kind_type t, const std::string& v, const location_type& l)
+        : Base (t)
+        , value (v)
+        , location (l)
+      {}
+#endif
+
+      /// Destroy the symbol.
+      ~basic_symbol ()
+      {
+        clear ();
+      }
+
+      /// Destroy contents, and record that is empty.
+      void clear ()
+      {
+        // User destructor.
+        symbol_kind_type yykind = this->kind ();
+        basic_symbol<Base>& yysym = *this;
+        (void) yysym;
+        switch (yykind)
+        {
+       default:
+          break;
+        }
+
+        // Value type destructor.
+switch (yykind)
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.template destroy< bool > ();
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.template destroy< enum condition > ();
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.template destroy< enum in_out_set > ();
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.template destroy< enum irq > ();
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.template destroy< enum mov > ();
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.template destroy< enum mov_op > ();
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.template destroy< int > ();
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.template destroy< std::shared_ptr<instruction> > ();
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.template destroy< std::shared_ptr<resolvable> > ();
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.template destroy< std::shared_ptr<symbol> > ();
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.template destroy< std::shared_ptr<wait_source> > ();
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.template destroy< std::string > ();
+        break;
+
+      default:
+        break;
+    }
+
+        Base::clear ();
+      }
+
+      /// The user-facing name of this symbol.
+      std::string name () const YY_NOEXCEPT
+      {
+        return parser::symbol_name (this->kind ());
+      }
+
+      /// Backward compatibility (Bison 3.6).
+      symbol_kind_type type_get () const YY_NOEXCEPT;
+
+      /// Whether empty.
+      bool empty () const YY_NOEXCEPT;
+
+      /// Destructive move, \a s is emptied into this.
+      void move (basic_symbol& s);
+
+      /// The semantic value.
+      semantic_type value;
+
+      /// The location.
+      location_type location;
+
+    private:
+#if YY_CPLUSPLUS < 201103L
+      /// Assignment operator.
+      basic_symbol& operator= (const basic_symbol& that);
+#endif
+    };
+
+    /// Type access provider for token (enum) based symbols.
+    struct by_kind
+    {
+      /// Default constructor.
+      by_kind ();
+
+#if 201103L <= YY_CPLUSPLUS
+      /// Move constructor.
+      by_kind (by_kind&& that);
+#endif
+
+      /// Copy constructor.
+      by_kind (const by_kind& that);
+
+      /// The symbol kind as needed by the constructor.
+      typedef token_kind_type kind_type;
+
+      /// Constructor from (external) token numbers.
+      by_kind (kind_type t);
+
+      /// Record that this symbol is empty.
+      void clear ();
+
+      /// Steal the symbol kind from \a that.
+      void move (by_kind& that);
+
+      /// The (internal) type number (corresponding to \a type).
+      /// \a empty when empty.
+      symbol_kind_type kind () const YY_NOEXCEPT;
+
+      /// Backward compatibility (Bison 3.6).
+      symbol_kind_type type_get () const YY_NOEXCEPT;
+
+      /// The symbol kind.
+      /// \a S_YYEMPTY when empty.
+      symbol_kind_type kind_;
+    };
+
+    /// Backward compatibility for a private implementation detail (Bison 3.6).
+    typedef by_kind by_type;
+
+    /// "External" symbols: returned by the scanner.
+    struct symbol_type : basic_symbol<by_kind>
+    {
+      /// Superclass.
+      typedef basic_symbol<by_kind> super_type;
+
+      /// Empty symbol.
+      symbol_type () {}
+
+      /// Constructor for valueless symbols, and symbols from each type.
+#if 201103L <= YY_CPLUSPLUS
+      symbol_type (int tok, location_type l)
+        : super_type(token_type (tok), std::move (l))
+      {
+        YY_ASSERT (tok == token::TOK_END || tok == token::TOK_YYerror || tok == token::TOK_YYUNDEF || tok == token::TOK_NEWLINE || tok == token::TOK_COMMA || tok == token::TOK_COLON || tok == token::TOK_LPAREN || tok == token::TOK_RPAREN || tok == token::TOK_LBRACKET || tok == token::TOK_RBRACKET || tok == token::TOK_PLUS || tok == token::TOK_MINUS || tok == token::TOK_MULTIPLY || tok == token::TOK_DIVIDE || tok == token::TOK_OR || tok == token::TOK_AND || tok == token::TOK_XOR || tok == token::TOK_POST_DECREMENT || tok == token::TOK_NOT_EQUAL || tok == token::TOK_NOT || tok == token::TOK_REVERSE || tok == token::TOK_EQUAL || tok == token::TOK_PROGRAM || tok == token::TOK_WRAP_TARGET || tok == token::TOK_WRAP || tok == token::TOK_DEFINE || tok == token::TOK_SIDE_SET || tok == token::TOK_WORD || tok == token::TOK_ORIGIN || tok == token::TOK_LANG_OPT || tok == token::TOK_JMP || tok == token::TOK_WAIT || tok == token::TOK_IN || tok == token::TOK_OUT || tok == token::TOK_PUSH || tok == token::TOK_PULL || tok == token::TOK_MOV || tok == token::TOK_IRQ || tok == token::TOK_SET || tok == token::TOK_NOP || tok == token::TOK_PIN || tok == token::TOK_GPIO || tok == token::TOK_OSRE || tok == token::TOK_PINS || tok == token::TOK_NULL || tok == token::TOK_PINDIRS || tok == token::TOK_BLOCK || tok == token::TOK_NOBLOCK || tok == token::TOK_IFEMPTY || tok == token::TOK_IFFULL || tok == token::TOK_NOWAIT || tok == token::TOK_CLEAR || tok == token::TOK_REL || tok == token::TOK_X || tok == token::TOK_Y || tok == token::TOK_EXEC || tok == token::TOK_PC || tok == token::TOK_ISR || tok == token::TOK_OSR || tok == token::TOK_OPTIONAL || tok == token::TOK_SIDE || tok == token::TOK_STATUS || tok == token::TOK_PUBLIC);
+      }
+#else
+      symbol_type (int tok, const location_type& l)
+        : super_type(token_type (tok), l)
+      {
+        YY_ASSERT (tok == token::TOK_END || tok == token::TOK_YYerror || tok == token::TOK_YYUNDEF || tok == token::TOK_NEWLINE || tok == token::TOK_COMMA || tok == token::TOK_COLON || tok == token::TOK_LPAREN || tok == token::TOK_RPAREN || tok == token::TOK_LBRACKET || tok == token::TOK_RBRACKET || tok == token::TOK_PLUS || tok == token::TOK_MINUS || tok == token::TOK_MULTIPLY || tok == token::TOK_DIVIDE || tok == token::TOK_OR || tok == token::TOK_AND || tok == token::TOK_XOR || tok == token::TOK_POST_DECREMENT || tok == token::TOK_NOT_EQUAL || tok == token::TOK_NOT || tok == token::TOK_REVERSE || tok == token::TOK_EQUAL || tok == token::TOK_PROGRAM || tok == token::TOK_WRAP_TARGET || tok == token::TOK_WRAP || tok == token::TOK_DEFINE || tok == token::TOK_SIDE_SET || tok == token::TOK_WORD || tok == token::TOK_ORIGIN || tok == token::TOK_LANG_OPT || tok == token::TOK_JMP || tok == token::TOK_WAIT || tok == token::TOK_IN || tok == token::TOK_OUT || tok == token::TOK_PUSH || tok == token::TOK_PULL || tok == token::TOK_MOV || tok == token::TOK_IRQ || tok == token::TOK_SET || tok == token::TOK_NOP || tok == token::TOK_PIN || tok == token::TOK_GPIO || tok == token::TOK_OSRE || tok == token::TOK_PINS || tok == token::TOK_NULL || tok == token::TOK_PINDIRS || tok == token::TOK_BLOCK || tok == token::TOK_NOBLOCK || tok == token::TOK_IFEMPTY || tok == token::TOK_IFFULL || tok == token::TOK_NOWAIT || tok == token::TOK_CLEAR || tok == token::TOK_REL || tok == token::TOK_X || tok == token::TOK_Y || tok == token::TOK_EXEC || tok == token::TOK_PC || tok == token::TOK_ISR || tok == token::TOK_OSR || tok == token::TOK_OPTIONAL || tok == token::TOK_SIDE || tok == token::TOK_STATUS || tok == token::TOK_PUBLIC);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      symbol_type (int tok, int v, location_type l)
+        : super_type(token_type (tok), std::move (v), std::move (l))
+      {
+        YY_ASSERT (tok == token::TOK_INT);
+      }
+#else
+      symbol_type (int tok, const int& v, const location_type& l)
+        : super_type(token_type (tok), v, l)
+      {
+        YY_ASSERT (tok == token::TOK_INT);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      symbol_type (int tok, std::string v, location_type l)
+        : super_type(token_type (tok), std::move (v), std::move (l))
+      {
+        YY_ASSERT (tok == token::TOK_ID || tok == token::TOK_STRING || tok == token::TOK_NON_WS || tok == token::TOK_CODE_BLOCK_START || tok == token::TOK_CODE_BLOCK_CONTENTS || tok == token::TOK_UNKNOWN_DIRECTIVE);
+      }
+#else
+      symbol_type (int tok, const std::string& v, const location_type& l)
+        : super_type(token_type (tok), v, l)
+      {
+        YY_ASSERT (tok == token::TOK_ID || tok == token::TOK_STRING || tok == token::TOK_NON_WS || tok == token::TOK_CODE_BLOCK_START || tok == token::TOK_CODE_BLOCK_CONTENTS || tok == token::TOK_UNKNOWN_DIRECTIVE);
+      }
+#endif
+    };
+
+    /// Build a parser object.
+    parser (pio_assembler& pioasm_yyarg);
+    virtual ~parser ();
+
+#if 201103L <= YY_CPLUSPLUS
+    /// Non copyable.
+    parser (const parser&) = delete;
+    /// Non copyable.
+    parser& operator= (const parser&) = delete;
+#endif
+
+    /// Parse.  An alias for parse ().
+    /// \returns  0 iff parsing succeeded.
+    int operator() ();
+
+    /// Parse.
+    /// \returns  0 iff parsing succeeded.
+    virtual int parse ();
+
+#if YYDEBUG
+    /// The current debugging stream.
+    std::ostream& debug_stream () const YY_ATTRIBUTE_PURE;
+    /// Set the current debugging stream.
+    void set_debug_stream (std::ostream &);
+
+    /// Type for debugging levels.
+    typedef int debug_level_type;
+    /// The current debugging level.
+    debug_level_type debug_level () const YY_ATTRIBUTE_PURE;
+    /// Set the current debugging level.
+    void set_debug_level (debug_level_type l);
+#endif
+
+    /// Report a syntax error.
+    /// \param loc    where the syntax error is found.
+    /// \param msg    a description of the syntax error.
+    virtual void error (const location_type& loc, const std::string& msg);
+
+    /// Report a syntax error.
+    void error (const syntax_error& err);
+
+    /// The user-facing name of the symbol whose (internal) number is
+    /// YYSYMBOL.  No bounds checking.
+    static std::string symbol_name (symbol_kind_type yysymbol);
+
+    // Implementation of make_symbol for each symbol type.
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_END (location_type l)
+      {
+        return symbol_type (token::TOK_END, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_END (const location_type& l)
+      {
+        return symbol_type (token::TOK_END, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_YYerror (location_type l)
+      {
+        return symbol_type (token::TOK_YYerror, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_YYerror (const location_type& l)
+      {
+        return symbol_type (token::TOK_YYerror, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_YYUNDEF (location_type l)
+      {
+        return symbol_type (token::TOK_YYUNDEF, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_YYUNDEF (const location_type& l)
+      {
+        return symbol_type (token::TOK_YYUNDEF, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NEWLINE (location_type l)
+      {
+        return symbol_type (token::TOK_NEWLINE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NEWLINE (const location_type& l)
+      {
+        return symbol_type (token::TOK_NEWLINE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_COMMA (location_type l)
+      {
+        return symbol_type (token::TOK_COMMA, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_COMMA (const location_type& l)
+      {
+        return symbol_type (token::TOK_COMMA, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_COLON (location_type l)
+      {
+        return symbol_type (token::TOK_COLON, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_COLON (const location_type& l)
+      {
+        return symbol_type (token::TOK_COLON, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_LPAREN (location_type l)
+      {
+        return symbol_type (token::TOK_LPAREN, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_LPAREN (const location_type& l)
+      {
+        return symbol_type (token::TOK_LPAREN, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_RPAREN (location_type l)
+      {
+        return symbol_type (token::TOK_RPAREN, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_RPAREN (const location_type& l)
+      {
+        return symbol_type (token::TOK_RPAREN, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_LBRACKET (location_type l)
+      {
+        return symbol_type (token::TOK_LBRACKET, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_LBRACKET (const location_type& l)
+      {
+        return symbol_type (token::TOK_LBRACKET, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_RBRACKET (location_type l)
+      {
+        return symbol_type (token::TOK_RBRACKET, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_RBRACKET (const location_type& l)
+      {
+        return symbol_type (token::TOK_RBRACKET, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PLUS (location_type l)
+      {
+        return symbol_type (token::TOK_PLUS, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PLUS (const location_type& l)
+      {
+        return symbol_type (token::TOK_PLUS, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_MINUS (location_type l)
+      {
+        return symbol_type (token::TOK_MINUS, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_MINUS (const location_type& l)
+      {
+        return symbol_type (token::TOK_MINUS, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_MULTIPLY (location_type l)
+      {
+        return symbol_type (token::TOK_MULTIPLY, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_MULTIPLY (const location_type& l)
+      {
+        return symbol_type (token::TOK_MULTIPLY, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_DIVIDE (location_type l)
+      {
+        return symbol_type (token::TOK_DIVIDE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_DIVIDE (const location_type& l)
+      {
+        return symbol_type (token::TOK_DIVIDE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_OR (location_type l)
+      {
+        return symbol_type (token::TOK_OR, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_OR (const location_type& l)
+      {
+        return symbol_type (token::TOK_OR, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_AND (location_type l)
+      {
+        return symbol_type (token::TOK_AND, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_AND (const location_type& l)
+      {
+        return symbol_type (token::TOK_AND, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_XOR (location_type l)
+      {
+        return symbol_type (token::TOK_XOR, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_XOR (const location_type& l)
+      {
+        return symbol_type (token::TOK_XOR, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_POST_DECREMENT (location_type l)
+      {
+        return symbol_type (token::TOK_POST_DECREMENT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_POST_DECREMENT (const location_type& l)
+      {
+        return symbol_type (token::TOK_POST_DECREMENT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NOT_EQUAL (location_type l)
+      {
+        return symbol_type (token::TOK_NOT_EQUAL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NOT_EQUAL (const location_type& l)
+      {
+        return symbol_type (token::TOK_NOT_EQUAL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NOT (location_type l)
+      {
+        return symbol_type (token::TOK_NOT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NOT (const location_type& l)
+      {
+        return symbol_type (token::TOK_NOT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_REVERSE (location_type l)
+      {
+        return symbol_type (token::TOK_REVERSE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_REVERSE (const location_type& l)
+      {
+        return symbol_type (token::TOK_REVERSE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_EQUAL (location_type l)
+      {
+        return symbol_type (token::TOK_EQUAL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_EQUAL (const location_type& l)
+      {
+        return symbol_type (token::TOK_EQUAL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PROGRAM (location_type l)
+      {
+        return symbol_type (token::TOK_PROGRAM, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PROGRAM (const location_type& l)
+      {
+        return symbol_type (token::TOK_PROGRAM, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_WRAP_TARGET (location_type l)
+      {
+        return symbol_type (token::TOK_WRAP_TARGET, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_WRAP_TARGET (const location_type& l)
+      {
+        return symbol_type (token::TOK_WRAP_TARGET, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_WRAP (location_type l)
+      {
+        return symbol_type (token::TOK_WRAP, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_WRAP (const location_type& l)
+      {
+        return symbol_type (token::TOK_WRAP, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_DEFINE (location_type l)
+      {
+        return symbol_type (token::TOK_DEFINE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_DEFINE (const location_type& l)
+      {
+        return symbol_type (token::TOK_DEFINE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_SIDE_SET (location_type l)
+      {
+        return symbol_type (token::TOK_SIDE_SET, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_SIDE_SET (const location_type& l)
+      {
+        return symbol_type (token::TOK_SIDE_SET, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_WORD (location_type l)
+      {
+        return symbol_type (token::TOK_WORD, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_WORD (const location_type& l)
+      {
+        return symbol_type (token::TOK_WORD, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_ORIGIN (location_type l)
+      {
+        return symbol_type (token::TOK_ORIGIN, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_ORIGIN (const location_type& l)
+      {
+        return symbol_type (token::TOK_ORIGIN, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_LANG_OPT (location_type l)
+      {
+        return symbol_type (token::TOK_LANG_OPT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_LANG_OPT (const location_type& l)
+      {
+        return symbol_type (token::TOK_LANG_OPT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_JMP (location_type l)
+      {
+        return symbol_type (token::TOK_JMP, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_JMP (const location_type& l)
+      {
+        return symbol_type (token::TOK_JMP, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_WAIT (location_type l)
+      {
+        return symbol_type (token::TOK_WAIT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_WAIT (const location_type& l)
+      {
+        return symbol_type (token::TOK_WAIT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_IN (location_type l)
+      {
+        return symbol_type (token::TOK_IN, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_IN (const location_type& l)
+      {
+        return symbol_type (token::TOK_IN, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_OUT (location_type l)
+      {
+        return symbol_type (token::TOK_OUT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_OUT (const location_type& l)
+      {
+        return symbol_type (token::TOK_OUT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PUSH (location_type l)
+      {
+        return symbol_type (token::TOK_PUSH, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PUSH (const location_type& l)
+      {
+        return symbol_type (token::TOK_PUSH, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PULL (location_type l)
+      {
+        return symbol_type (token::TOK_PULL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PULL (const location_type& l)
+      {
+        return symbol_type (token::TOK_PULL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_MOV (location_type l)
+      {
+        return symbol_type (token::TOK_MOV, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_MOV (const location_type& l)
+      {
+        return symbol_type (token::TOK_MOV, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_IRQ (location_type l)
+      {
+        return symbol_type (token::TOK_IRQ, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_IRQ (const location_type& l)
+      {
+        return symbol_type (token::TOK_IRQ, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_SET (location_type l)
+      {
+        return symbol_type (token::TOK_SET, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_SET (const location_type& l)
+      {
+        return symbol_type (token::TOK_SET, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NOP (location_type l)
+      {
+        return symbol_type (token::TOK_NOP, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NOP (const location_type& l)
+      {
+        return symbol_type (token::TOK_NOP, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PIN (location_type l)
+      {
+        return symbol_type (token::TOK_PIN, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PIN (const location_type& l)
+      {
+        return symbol_type (token::TOK_PIN, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_GPIO (location_type l)
+      {
+        return symbol_type (token::TOK_GPIO, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_GPIO (const location_type& l)
+      {
+        return symbol_type (token::TOK_GPIO, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_OSRE (location_type l)
+      {
+        return symbol_type (token::TOK_OSRE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_OSRE (const location_type& l)
+      {
+        return symbol_type (token::TOK_OSRE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PINS (location_type l)
+      {
+        return symbol_type (token::TOK_PINS, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PINS (const location_type& l)
+      {
+        return symbol_type (token::TOK_PINS, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NULL (location_type l)
+      {
+        return symbol_type (token::TOK_NULL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NULL (const location_type& l)
+      {
+        return symbol_type (token::TOK_NULL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PINDIRS (location_type l)
+      {
+        return symbol_type (token::TOK_PINDIRS, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PINDIRS (const location_type& l)
+      {
+        return symbol_type (token::TOK_PINDIRS, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_BLOCK (location_type l)
+      {
+        return symbol_type (token::TOK_BLOCK, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_BLOCK (const location_type& l)
+      {
+        return symbol_type (token::TOK_BLOCK, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NOBLOCK (location_type l)
+      {
+        return symbol_type (token::TOK_NOBLOCK, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NOBLOCK (const location_type& l)
+      {
+        return symbol_type (token::TOK_NOBLOCK, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_IFEMPTY (location_type l)
+      {
+        return symbol_type (token::TOK_IFEMPTY, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_IFEMPTY (const location_type& l)
+      {
+        return symbol_type (token::TOK_IFEMPTY, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_IFFULL (location_type l)
+      {
+        return symbol_type (token::TOK_IFFULL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_IFFULL (const location_type& l)
+      {
+        return symbol_type (token::TOK_IFFULL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NOWAIT (location_type l)
+      {
+        return symbol_type (token::TOK_NOWAIT, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NOWAIT (const location_type& l)
+      {
+        return symbol_type (token::TOK_NOWAIT, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_CLEAR (location_type l)
+      {
+        return symbol_type (token::TOK_CLEAR, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_CLEAR (const location_type& l)
+      {
+        return symbol_type (token::TOK_CLEAR, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_REL (location_type l)
+      {
+        return symbol_type (token::TOK_REL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_REL (const location_type& l)
+      {
+        return symbol_type (token::TOK_REL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_X (location_type l)
+      {
+        return symbol_type (token::TOK_X, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_X (const location_type& l)
+      {
+        return symbol_type (token::TOK_X, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_Y (location_type l)
+      {
+        return symbol_type (token::TOK_Y, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_Y (const location_type& l)
+      {
+        return symbol_type (token::TOK_Y, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_EXEC (location_type l)
+      {
+        return symbol_type (token::TOK_EXEC, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_EXEC (const location_type& l)
+      {
+        return symbol_type (token::TOK_EXEC, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PC (location_type l)
+      {
+        return symbol_type (token::TOK_PC, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PC (const location_type& l)
+      {
+        return symbol_type (token::TOK_PC, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_ISR (location_type l)
+      {
+        return symbol_type (token::TOK_ISR, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_ISR (const location_type& l)
+      {
+        return symbol_type (token::TOK_ISR, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_OSR (location_type l)
+      {
+        return symbol_type (token::TOK_OSR, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_OSR (const location_type& l)
+      {
+        return symbol_type (token::TOK_OSR, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_OPTIONAL (location_type l)
+      {
+        return symbol_type (token::TOK_OPTIONAL, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_OPTIONAL (const location_type& l)
+      {
+        return symbol_type (token::TOK_OPTIONAL, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_SIDE (location_type l)
+      {
+        return symbol_type (token::TOK_SIDE, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_SIDE (const location_type& l)
+      {
+        return symbol_type (token::TOK_SIDE, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_STATUS (location_type l)
+      {
+        return symbol_type (token::TOK_STATUS, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_STATUS (const location_type& l)
+      {
+        return symbol_type (token::TOK_STATUS, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_PUBLIC (location_type l)
+      {
+        return symbol_type (token::TOK_PUBLIC, std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_PUBLIC (const location_type& l)
+      {
+        return symbol_type (token::TOK_PUBLIC, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_ID (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_ID, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_ID (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_ID, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_STRING (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_STRING, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_STRING (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_STRING, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_NON_WS (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_NON_WS, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_NON_WS (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_NON_WS, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_CODE_BLOCK_START (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_CODE_BLOCK_START, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_CODE_BLOCK_START (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_CODE_BLOCK_START, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_CODE_BLOCK_CONTENTS (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_CODE_BLOCK_CONTENTS, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_CODE_BLOCK_CONTENTS (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_CODE_BLOCK_CONTENTS, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_UNKNOWN_DIRECTIVE (std::string v, location_type l)
+      {
+        return symbol_type (token::TOK_UNKNOWN_DIRECTIVE, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_UNKNOWN_DIRECTIVE (const std::string& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_UNKNOWN_DIRECTIVE, v, l);
+      }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+      static
+      symbol_type
+      make_INT (int v, location_type l)
+      {
+        return symbol_type (token::TOK_INT, std::move (v), std::move (l));
+      }
+#else
+      static
+      symbol_type
+      make_INT (const int& v, const location_type& l)
+      {
+        return symbol_type (token::TOK_INT, v, l);
+      }
+#endif
+
+
+    class context
+    {
+    public:
+      context (const parser& yyparser, const symbol_type& yyla);
+      const symbol_type& lookahead () const { return yyla_; }
+      symbol_kind_type token () const { return yyla_.kind (); }
+      const location_type& location () const { return yyla_.location; }
+
+      /// Put in YYARG at most YYARGN of the expected tokens, and return the
+      /// number of tokens stored in YYARG.  If YYARG is null, return the
+      /// number of expected tokens (guaranteed to be less than YYNTOKENS).
+      int expected_tokens (symbol_kind_type yyarg[], int yyargn) const;
+
+    private:
+      const parser& yyparser_;
+      const symbol_type& yyla_;
+    };
+
+  private:
+#if YY_CPLUSPLUS < 201103L
+    /// Non copyable.
+    parser (const parser&);
+    /// Non copyable.
+    parser& operator= (const parser&);
+#endif
+
+    /// Check the lookahead yytoken.
+    /// \returns  true iff the token will be eventually shifted.
+    bool yy_lac_check_ (symbol_kind_type yytoken) const;
+    /// Establish the initial context if no initial context currently exists.
+    /// \returns  true iff the token will be eventually shifted.
+    bool yy_lac_establish_ (symbol_kind_type yytoken);
+    /// Discard any previous initial lookahead context because of event.
+    /// \param event  the event which caused the lookahead to be discarded.
+    ///               Only used for debbuging output.
+    void yy_lac_discard_ (const char* event);
+
+    /// Stored state numbers (used for stacks).
+    typedef unsigned char state_type;
+
+    /// The arguments of the error message.
+    int yy_syntax_error_arguments_ (const context& yyctx,
+                                    symbol_kind_type yyarg[], int yyargn) const;
+
+    /// Generate an error message.
+    /// \param yyctx     the context in which the error occurred.
+    virtual std::string yysyntax_error_ (const context& yyctx) const;
+    /// Compute post-reduction state.
+    /// \param yystate   the current state
+    /// \param yysym     the nonterminal to push on the stack
+    static state_type yy_lr_goto_state_ (state_type yystate, int yysym);
+
+    /// Whether the given \c yypact_ value indicates a defaulted state.
+    /// \param yyvalue   the value to check
+    static bool yy_pact_value_is_default_ (int yyvalue);
+
+    /// Whether the given \c yytable_ value indicates a syntax error.
+    /// \param yyvalue   the value to check
+    static bool yy_table_value_is_error_ (int yyvalue);
+
+    static const signed char yypact_ninf_;
+    static const signed char yytable_ninf_;
+
+    /// Convert a scanner token kind \a t to a symbol kind.
+    /// In theory \a t should be a token_kind_type, but character literals
+    /// are valid, yet not members of the token_type enum.
+    static symbol_kind_type yytranslate_ (int t);
+
+    /// Convert the symbol name \a n to a form suitable for a diagnostic.
+    static std::string yytnamerr_ (const char *yystr);
+
+    /// For a symbol, its name in clear.
+    static const char* const yytname_[];
+
+
+    // Tables.
+    // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+    // STATE-NUM.
+    static const short yypact_[];
+
+    // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+    // Performed when YYTABLE does not specify something else to do.  Zero
+    // means the default is an error.
+    static const signed char yydefact_[];
+
+    // YYPGOTO[NTERM-NUM].
+    static const short yypgoto_[];
+
+    // YYDEFGOTO[NTERM-NUM].
+    static const short yydefgoto_[];
+
+    // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+    // positive, shift that token.  If negative, reduce the rule whose
+    // number is the opposite.  If YYTABLE_NINF, syntax error.
+    static const short yytable_[];
+
+    static const unsigned char yycheck_[];
+
+    // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+    // symbol of state STATE-NUM.
+    static const signed char yystos_[];
+
+    // YYR1[YYN] -- Symbol number of symbol that rule YYN derives.
+    static const signed char yyr1_[];
+
+    // YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.
+    static const signed char yyr2_[];
+
+
+#if YYDEBUG
+    // YYRLINE[YYN] -- Source line where rule number YYN was defined.
+    static const short yyrline_[];
+    /// Report on the debug stream that the rule \a r is going to be reduced.
+    virtual void yy_reduce_print_ (int r) const;
+    /// Print the state stack on the debug stream.
+    virtual void yy_stack_print_ () const;
+
+    /// Debugging level.
+    int yydebug_;
+    /// Debug stream.
+    std::ostream* yycdebug_;
+
+    /// \brief Display a symbol kind, value and location.
+    /// \param yyo    The output stream.
+    /// \param yysym  The symbol.
+    template <typename Base>
+    void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const;
+#endif
+
+    /// \brief Reclaim the memory associated to a symbol.
+    /// \param yymsg     Why this token is reclaimed.
+    ///                  If null, print nothing.
+    /// \param yysym     The symbol.
+    template <typename Base>
+    void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const;
+
+  private:
+    /// Type access provider for state based symbols.
+    struct by_state
+    {
+      /// Default constructor.
+      by_state () YY_NOEXCEPT;
+
+      /// The symbol kind as needed by the constructor.
+      typedef state_type kind_type;
+
+      /// Constructor.
+      by_state (kind_type s) YY_NOEXCEPT;
+
+      /// Copy constructor.
+      by_state (const by_state& that) YY_NOEXCEPT;
+
+      /// Record that this symbol is empty.
+      void clear () YY_NOEXCEPT;
+
+      /// Steal the symbol kind from \a that.
+      void move (by_state& that);
+
+      /// The symbol kind (corresponding to \a state).
+      /// \a symbol_kind::S_YYEMPTY when empty.
+      symbol_kind_type kind () const YY_NOEXCEPT;
+
+      /// The state number used to denote an empty symbol.
+      /// We use the initial state, as it does not have a value.
+      enum { empty_state = 0 };
+
+      /// The state.
+      /// \a empty when empty.
+      state_type state;
+    };
+
+    /// "Internal" symbol: element of the stack.
+    struct stack_symbol_type : basic_symbol<by_state>
+    {
+      /// Superclass.
+      typedef basic_symbol<by_state> super_type;
+      /// Construct an empty symbol.
+      stack_symbol_type ();
+      /// Move or copy construction.
+      stack_symbol_type (YY_RVREF (stack_symbol_type) that);
+      /// Steal the contents from \a sym to build this.
+      stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym);
+#if YY_CPLUSPLUS < 201103L
+      /// Assignment, needed by push_back by some old implementations.
+      /// Moves the contents of that.
+      stack_symbol_type& operator= (stack_symbol_type& that);
+
+      /// Assignment, needed by push_back by other implementations.
+      /// Needed by some other old implementations.
+      stack_symbol_type& operator= (const stack_symbol_type& that);
+#endif
+    };
+
+    /// A stack with random access from its top.
+    template <typename T, typename S = std::vector<T> >
+    class stack
+    {
+    public:
+      // Hide our reversed order.
+      typedef typename S::iterator iterator;
+      typedef typename S::const_iterator const_iterator;
+      typedef typename S::size_type size_type;
+      typedef typename std::ptrdiff_t index_type;
+
+      stack (size_type n = 200)
+        : seq_ (n)
+      {}
+
+#if 201103L <= YY_CPLUSPLUS
+      /// Non copyable.
+      stack (const stack&) = delete;
+      /// Non copyable.
+      stack& operator= (const stack&) = delete;
+#endif
+
+      /// Random access.
+      ///
+      /// Index 0 returns the topmost element.
+      const T&
+      operator[] (index_type i) const
+      {
+        return seq_[size_type (size () - 1 - i)];
+      }
+
+      /// Random access.
+      ///
+      /// Index 0 returns the topmost element.
+      T&
+      operator[] (index_type i)
+      {
+        return seq_[size_type (size () - 1 - i)];
+      }
+
+      /// Steal the contents of \a t.
+      ///
+      /// Close to move-semantics.
+      void
+      push (YY_MOVE_REF (T) t)
+      {
+        seq_.push_back (T ());
+        operator[] (0).move (t);
+      }
+
+      /// Pop elements from the stack.
+      void
+      pop (std::ptrdiff_t n = 1) YY_NOEXCEPT
+      {
+        for (; 0 < n; --n)
+          seq_.pop_back ();
+      }
+
+      /// Pop all elements from the stack.
+      void
+      clear () YY_NOEXCEPT
+      {
+        seq_.clear ();
+      }
+
+      /// Number of elements on the stack.
+      index_type
+      size () const YY_NOEXCEPT
+      {
+        return index_type (seq_.size ());
+      }
+
+      /// Iterator on top of the stack (going downwards).
+      const_iterator
+      begin () const YY_NOEXCEPT
+      {
+        return seq_.begin ();
+      }
+
+      /// Bottom of the stack.
+      const_iterator
+      end () const YY_NOEXCEPT
+      {
+        return seq_.end ();
+      }
+
+      /// Present a slice of the top of a stack.
+      class slice
+      {
+      public:
+        slice (const stack& stack, index_type range)
+          : stack_ (stack)
+          , range_ (range)
+        {}
+
+        const T&
+        operator[] (index_type i) const
+        {
+          return stack_[range_ - i];
+        }
+
+      private:
+        const stack& stack_;
+        index_type range_;
+      };
+
+    private:
+#if YY_CPLUSPLUS < 201103L
+      /// Non copyable.
+      stack (const stack&);
+      /// Non copyable.
+      stack& operator= (const stack&);
+#endif
+      /// The wrapped container.
+      S seq_;
+    };
+
+
+    /// Stack type.
+    typedef stack<stack_symbol_type> stack_type;
+
+    /// The stack.
+    stack_type yystack_;
+    /// The stack for LAC.
+    /// Logically, the yy_lac_stack's lifetime is confined to the function
+    /// yy_lac_check_. We just store it as a member of this class to hold
+    /// on to the memory and to avoid frequent reallocations.
+    /// Since yy_lac_check_ is const, this member must be mutable.
+    mutable std::vector<state_type> yylac_stack_;
+    /// Whether an initial LAC context was established.
+    bool yy_lac_established_;
+
+
+    /// Push a new state on the stack.
+    /// \param m    a debug message to display
+    ///             if null, no trace is output.
+    /// \param sym  the symbol
+    /// \warning the contents of \a s.value is stolen.
+    void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym);
+
+    /// Push a new look ahead token on the state on the stack.
+    /// \param m    a debug message to display
+    ///             if null, no trace is output.
+    /// \param s    the state
+    /// \param sym  the symbol (for its value and location).
+    /// \warning the contents of \a sym.value is stolen.
+    void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym);
+
+    /// Pop \a n symbols from the stack.
+    void yypop_ (int n = 1);
+
+    /// Constants.
+    enum
+    {
+      yylast_ = 205,     ///< Last index in yytable_.
+      yynnts_ = 27,  ///< Number of nonterminal symbols.
+      yyfinal_ = 96 ///< Termination state number.
+    };
+
+
+    // User arguments.
+    pio_assembler& pioasm;
+
+  };
+
+  inline
+  parser::symbol_kind_type
+  parser::yytranslate_ (int t)
+  {
+    // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to
+    // TOKEN-NUM as returned by yylex.
+    static
+    const signed char
+    translate_table[] =
+    {
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69
+    };
+    // Last valid token kind.
+    const int code_max = 324;
+
+    if (t <= 0)
+      return symbol_kind::S_YYEOF;
+    else if (t <= code_max)
+      return YY_CAST (symbol_kind_type, translate_table[t]);
+    else
+      return symbol_kind::S_YYUNDEF;
+  }
+
+  // basic_symbol.
+  template <typename Base>
+  parser::basic_symbol<Base>::basic_symbol (const basic_symbol& that)
+    : Base (that)
+    , value ()
+    , location (that.location)
+  {
+    switch (this->kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.copy< bool > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.copy< enum condition > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.copy< enum in_out_set > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.copy< enum irq > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.copy< enum mov > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.copy< enum mov_op > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.copy< int > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.copy< std::shared_ptr<instruction> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.copy< std::shared_ptr<resolvable> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.copy< std::shared_ptr<symbol> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.copy< std::shared_ptr<wait_source> > (YY_MOVE (that.value));
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.copy< std::string > (YY_MOVE (that.value));
+        break;
+
+      default:
+        break;
+    }
+
+  }
+
+
+
+  template <typename Base>
+  parser::symbol_kind_type
+  parser::basic_symbol<Base>::type_get () const YY_NOEXCEPT
+  {
+    return this->kind ();
+  }
+
+  template <typename Base>
+  bool
+  parser::basic_symbol<Base>::empty () const YY_NOEXCEPT
+  {
+    return this->kind () == symbol_kind::S_YYEMPTY;
+  }
+
+  template <typename Base>
+  void
+  parser::basic_symbol<Base>::move (basic_symbol& s)
+  {
+    super_type::move (s);
+    switch (this->kind ())
+    {
+      case symbol_kind::S_if_full: // if_full
+      case symbol_kind::S_if_empty: // if_empty
+      case symbol_kind::S_blocking: // blocking
+        value.move< bool > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_condition: // condition
+        value.move< enum condition > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_in_source: // in_source
+      case symbol_kind::S_out_target: // out_target
+      case symbol_kind::S_set_target: // set_target
+        value.move< enum in_out_set > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_irq_modifiers: // irq_modifiers
+        value.move< enum irq > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_mov_target: // mov_target
+      case symbol_kind::S_mov_source: // mov_source
+        value.move< enum mov > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_mov_op: // mov_op
+        value.move< enum mov_op > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_INT: // "integer"
+        value.move< int > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_instruction: // instruction
+      case symbol_kind::S_base_instruction: // base_instruction
+        value.move< std::shared_ptr<instruction> > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_value: // value
+      case symbol_kind::S_expression: // expression
+      case symbol_kind::S_delay: // delay
+      case symbol_kind::S_sideset: // sideset
+        value.move< std::shared_ptr<resolvable> > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_label_decl: // label_decl
+      case symbol_kind::S_symbol_def: // symbol_def
+        value.move< std::shared_ptr<symbol> > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_wait_source: // wait_source
+        value.move< std::shared_ptr<wait_source> > (YY_MOVE (s.value));
+        break;
+
+      case symbol_kind::S_ID: // "identifier"
+      case symbol_kind::S_STRING: // "string"
+      case symbol_kind::S_NON_WS: // "text"
+      case symbol_kind::S_CODE_BLOCK_START: // "code block"
+      case symbol_kind::S_CODE_BLOCK_CONTENTS: // "%}"
+      case symbol_kind::S_UNKNOWN_DIRECTIVE: // UNKNOWN_DIRECTIVE
+        value.move< std::string > (YY_MOVE (s.value));
+        break;
+
+      default:
+        break;
+    }
+
+    location = YY_MOVE (s.location);
+  }
+
+  // by_kind.
+  inline
+  parser::by_kind::by_kind ()
+    : kind_ (symbol_kind::S_YYEMPTY)
+  {}
+
+#if 201103L <= YY_CPLUSPLUS
+  inline
+  parser::by_kind::by_kind (by_kind&& that)
+    : kind_ (that.kind_)
+  {
+    that.clear ();
+  }
+#endif
+
+  inline
+  parser::by_kind::by_kind (const by_kind& that)
+    : kind_ (that.kind_)
+  {}
+
+  inline
+  parser::by_kind::by_kind (token_kind_type t)
+    : kind_ (yytranslate_ (t))
+  {}
+
+  inline
+  void
+  parser::by_kind::clear ()
+  {
+    kind_ = symbol_kind::S_YYEMPTY;
+  }
+
+  inline
+  void
+  parser::by_kind::move (by_kind& that)
+  {
+    kind_ = that.kind_;
+    that.clear ();
+  }
+
+  inline
+  parser::symbol_kind_type
+  parser::by_kind::kind () const YY_NOEXCEPT
+  {
+    return kind_;
+  }
+
+  inline
+  parser::symbol_kind_type
+  parser::by_kind::type_get () const YY_NOEXCEPT
+  {
+    return this->kind ();
+  }
+
+} // yy
+
+
+
+
+#endif // !YY_YY_HOME_GRAHAM_DEV_MU_PICO_SDK_TOOLS_PIOASM_GEN_PARSER_HPP_INCLUDED
diff --git a/tools/pioasm/hex_output.cpp b/tools/pioasm/hex_output.cpp
new file mode 100644
index 0000000..39dd0fa
--- /dev/null
+++ b/tools/pioasm/hex_output.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "output_format.h"
+#include <iostream>
+
+struct hex_output : public output_format {
+    struct factory {
+        factory() {
+            output_format::add(new hex_output());
+        }
+    };
+
+    hex_output() : output_format("hex") {}
+
+    std::string get_description() {
+        return "Raw hex output (only valid for single program inputs)";
+    }
+
+    virtual int output(std::string destination, std::vector<std::string> output_options,
+                       const compiled_source &source) {
+        FILE *out = open_single_output(destination);
+        if (!out) return 1;
+
+        if (source.programs.size() > 1) {
+            // todo don't have locations any more!
+            std::cerr << "error: hex output only supports a single program input\n";
+            return 1;
+        }
+        for (const auto &i : source.programs[0].instructions) {
+            fprintf(out, "%04x\n", i);
+        }
+        if (out != stdout) { fclose(out); }
+        return 0;
+    }
+};
+
+static hex_output::factory creator;
diff --git a/tools/pioasm/lexer.ll b/tools/pioasm/lexer.ll
new file mode 100644
index 0000000..939b06f
--- /dev/null
+++ b/tools/pioasm/lexer.ll
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+%{ /* -*- C++ -*- */
+# include <cerrno>
+# include <climits>
+# include <cstdlib>
+# include <cstring>
+# include <string>
+# include "pio_assembler.h"
+# include "parser.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4996) // fopen
+#endif
+
+%}
+
+%option noyywrap nounput noinput batch debug never-interactive case-insensitive noline
+
+%{
+  yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc);
+  yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc);
+  yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc);
+%}
+
+blank         [ \t]
+whitesp       {blank}+
+
+comment       (";"|"//")[^\n]*
+
+digit         [0-9]
+id            [a-zA-Z_][a-zA-Z0-9_]*
+
+binary        "0b"[01]+
+int           {digit}+
+hex	          "0x"[0-9a-fA-F]+
+directive     \.{id}
+
+output_fmt    [^%\n]+
+
+%{
+  // Code run each time a pattern is matched.
+  # define YY_USER_ACTION  loc.columns (yyleng);
+%}
+
+%x code_block
+%x c_comment
+%x lang_opt
+
+%%
+        std::string code_block_contents;
+        yy::location code_block_start;
+%{
+  // A handy shortcut to the location held by the pio_assembler.
+  yy::location& loc = pioasm.location;
+  // Code run each time yylex is called.
+  loc.step();
+%}
+
+{blank}+                            loc.step();
+\n+                                 { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline); }
+
+"%"{blank}*{output_fmt}{blank}*"{"  {
+                                        BEGIN(code_block);
+                                        code_block_contents = "";
+                                        code_block_start = loc;
+                                        std::string tmp(yytext);
+                                        tmp = tmp.substr(1, tmp.length() - 2);
+                                        tmp = tmp.erase(0, tmp.find_first_not_of(" \t"));
+                                        tmp = tmp.erase(tmp.find_last_not_of(" \t") + 1);
+                                        return yy::parser::make_CODE_BLOCK_START( tmp, loc);
+                                    }
+<code_block>{
+    {blank}+                        loc.step();
+    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
+    "%}"                            { BEGIN(INITIAL); auto loc2 = loc; loc2.begin = code_block_start.begin; return yy::parser::make_CODE_BLOCK_CONTENTS(code_block_contents, loc2); }
+    .*                              { code_block_contents += std::string(yytext) + "\n"; }
+}
+
+<c_comment>{
+    {blank}+                        loc.step();
+    "*/"                            { BEGIN(INITIAL); }
+    "*"                             { }
+    [^\n\*]*                        { }
+    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
+}
+
+<lang_opt>{
+\"[^\n]*\"                          return yy::parser::make_STRING(yytext, loc);
+{blank}+                            loc.step();
+"="		                            return yy::parser::make_EQUAL(loc);
+{int}                               return make_INT(yytext, loc);
+{hex}                               return make_HEX(yytext, loc);
+{binary}                            return make_BINARY(yytext, loc);
+[^ \t\n\"=]+                         return yy::parser::make_NON_WS(yytext, loc);
+\n+                                 { BEGIN(INITIAL); auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline);  }
+.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
+}
+
+"/*"                                { BEGIN(c_comment); }
+","	                                return yy::parser::make_COMMA(loc);
+"::"                                return yy::parser::make_REVERSE(loc);
+":"	                                return yy::parser::make_COLON(loc);
+"["                                 return yy::parser::make_LBRACKET(loc);
+"]"                                 return yy::parser::make_RBRACKET(loc);
+"("                                 return yy::parser::make_LPAREN(loc);
+")"                                 return yy::parser::make_RPAREN(loc);
+"+"                                 return yy::parser::make_PLUS(loc);
+"--"                                return yy::parser::make_POST_DECREMENT(loc);
+"−−"                                return yy::parser::make_POST_DECREMENT(loc);
+"-"                                 return yy::parser::make_MINUS(loc);
+"*"                                 return yy::parser::make_MULTIPLY(loc);
+"/"                                 return yy::parser::make_DIVIDE(loc);
+"|"                                 return yy::parser::make_OR(loc);
+"&"                                 return yy::parser::make_AND(loc);
+"^"                                 return yy::parser::make_XOR(loc);
+"!="		                        return yy::parser::make_NOT_EQUAL(loc);
+"!"			                        return yy::parser::make_NOT(loc);
+"~"			                        return yy::parser::make_NOT(loc);
+
+".program"		                    return yy::parser::make_PROGRAM(loc);
+".wrap_target"	                    return yy::parser::make_WRAP_TARGET(loc);
+".wrap"			                    return yy::parser::make_WRAP(loc);
+".word"			                    return yy::parser::make_WORD(loc);
+".define"		                    return yy::parser::make_DEFINE(loc);
+".side_set"		                    return yy::parser::make_SIDE_SET(loc);
+".origin"		                    return yy::parser::make_ORIGIN(loc);
+".lang_opt"         	            { BEGIN(lang_opt); return yy::parser::make_LANG_OPT(loc); }
+{directive}                         return yy::parser::make_UNKNOWN_DIRECTIVE(yytext, loc);
+
+"JMP"			                    return yy::parser::make_JMP(loc);
+"WAIT"			                    return yy::parser::make_WAIT(loc);
+"IN"			                    return yy::parser::make_IN(loc);
+"OUT"			                    return yy::parser::make_OUT(loc);
+"PUSH"			                    return yy::parser::make_PUSH(loc);
+"PULL"			                    return yy::parser::make_PULL(loc);
+"MOV"			                    return yy::parser::make_MOV(loc);
+"IRQ"			                    return yy::parser::make_IRQ(loc);
+"SET"			                    return yy::parser::make_SET(loc);
+"NOP"			                    return yy::parser::make_NOP(loc);
+
+"PUBLIC"		                    return yy::parser::make_PUBLIC(loc);
+
+"OPTIONAL"		                    return yy::parser::make_OPTIONAL(loc);
+"OPT"			                    return yy::parser::make_OPTIONAL(loc);
+"SIDE"			                    return yy::parser::make_SIDE(loc);
+"SIDESET"	                        return yy::parser::make_SIDE(loc);
+"SIDE_SET"   	                    return yy::parser::make_SIDE(loc);
+"PIN"			                    return yy::parser::make_PIN(loc);
+"GPIO"			                    return yy::parser::make_GPIO(loc);
+"OSRE"			                    return yy::parser::make_OSRE(loc);
+
+"PINS"			                    return yy::parser::make_PINS(loc);
+"NULL"			                    return yy::parser::make_NULL(loc);
+"PINDIRS"		                    return yy::parser::make_PINDIRS(loc);
+"X"	    		                    return yy::parser::make_X(loc);
+"Y"		    	                    return yy::parser::make_Y(loc);
+"PC"			                    return yy::parser::make_PC(loc);
+"EXEC"			                    return yy::parser::make_EXEC(loc);
+"ISR"			                    return yy::parser::make_ISR(loc);
+"OSR"			                    return yy::parser::make_OSR(loc);
+"STATUS"		                    return yy::parser::make_STATUS(loc);
+
+"BLOCK"			                    return yy::parser::make_BLOCK(loc);
+"NOBLOCK"		                    return yy::parser::make_NOBLOCK(loc);
+"IFFULL"		                    return yy::parser::make_IFFULL(loc);
+"IFEMPTY"		                    return yy::parser::make_IFEMPTY(loc);
+"REL"			                    return yy::parser::make_REL(loc);
+
+"CLEAR"			                    return yy::parser::make_CLEAR(loc);
+"NOWAIT"		                    return yy::parser::make_NOWAIT(loc);
+
+"ONE"                               return yy::parser::make_INT(1, loc);
+"ZERO"                              return yy::parser::make_INT(0, loc);
+
+<<EOF>>                             return yy::parser::make_END(loc);
+
+{int}                               return make_INT(yytext, loc);
+{hex}                               return make_HEX(yytext, loc);
+{binary}                            return make_BINARY(yytext, loc);
+
+{id}                                return yy::parser::make_ID(yytext, loc);
+
+{comment}                           { }
+
+.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
+
+%%
+
+yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str(), NULL, 10);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str() + 2, NULL, 16);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "hex is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc)
+{
+  errno = 0;
+  long n = strtol (s.c_str()+2, NULL, 2);
+  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
+    throw yy::parser::syntax_error (loc, "binary is out of range: " + s);
+  return yy::parser::make_INT((int) n, loc);
+}
+
+void pio_assembler::scan_begin ()
+{
+  yy_flex_debug = false;
+  if (source.empty () || source == "-")
+    yyin = stdin;
+  else if (!(yyin = fopen (source.c_str (), "r")))
+    {
+      std::cerr << "cannot open " << source << ": " << strerror(errno) << '\n';
+      exit (EXIT_FAILURE);
+    }
+}
+
+void pio_assembler::scan_end ()
+{
+  fclose (yyin);
+}
diff --git a/tools/pioasm/main.cpp b/tools/pioasm/main.cpp
new file mode 100644
index 0000000..e7ff686
--- /dev/null
+++ b/tools/pioasm/main.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <iostream>
+#include "pio_assembler.h"
+
+#define DEFAULT_OUTPUT_FORMAT "c-sdk"
+
+void usage() {
+    std::cerr << "usage: pioasm <options> <input> (<output>)\n\n";
+    std::cerr << "Assemble file of PIO program(s) for use in applications.\n";
+    std::cerr << "   <input>             the input filename\n";
+    std::cerr << "   <output>            the output filename (or filename prefix if the output format produces multiple outputs).\n";
+    std::cerr << "                       if not specified, a single output will be written to stdout\n";
+    std::cerr << "\n";
+    std::cerr << "options:\n";
+    std::cerr << "  -o <output_format>   select output_format (default '" << DEFAULT_OUTPUT_FORMAT << "'); available options are:\n";
+    for(const auto& f : output_format::all()) {
+        std::cerr << "                           " << f->name << std::endl;
+        std::cerr << "                               " << f->get_description() << std::endl;
+    }
+    std::cerr << "  -p <output_param>    add a parameter to be passed to the output format generator" << std::endl;
+    std::cerr << "  -?, --help           print this help and exit\n";
+}
+
+int main(int argc, char *argv[]) {
+    int res = 0;
+    pio_assembler pioasm;
+    std::string format(DEFAULT_OUTPUT_FORMAT);
+    const char *input = nullptr;
+    const char *output = nullptr;
+    std::vector<std::string> options;
+    int i = 1;
+    for (; !res && i < argc; i++) {
+        if (argv[i][0] != '-') break;
+        if (argv[i] == std::string("-o")) {
+            if (++i < argc) {
+                format = argv[i];
+            } else {
+                std::cerr << "error: -o requires format value" << std::endl;
+                res = 1;
+            }
+        } else if (argv[i] == std::string("-p")) {
+            if (++i < argc) {
+                options.emplace_back(argv[i]);
+            } else {
+                std::cerr << "error: -p requires parameter value" << std::endl;
+                res = 1;
+            }
+        } else if (argv[i] == std::string("-?") || argv[i] == std::string("--help")) {
+            usage();
+            return 1;
+        } else {
+            std::cerr << "error: unknown option " << argv[i] << std::endl;
+            res = 1;
+        }
+    }
+    if (!res) {
+        if (i != argc) {
+            input = argv[i++];
+        } else {
+            std::cerr << "error: expected input filename\n";
+            res = 1;
+        }
+    }
+    if (!res) {
+        if (i != argc) {
+            output = argv[i++];
+        } else {
+            output = "-";
+        }
+    }
+    if (!res && i != argc) {
+        std::cerr << "unexpected command line argument " << argv[i] << std::endl;
+        res = 1;
+    }
+    std::shared_ptr<output_format> oformat;
+    if (!res) {
+        const auto& e = std::find_if(output_format::all().begin(), output_format::all().end(),
+                                     [&](const std::shared_ptr<output_format> &f) {
+                                         return f->name == format;
+                                     });
+        if (e == output_format::all().end()) {
+            std::cerr << "error: unknown output format '" << format << "'" << std::endl;
+            res = 1;
+        } else {
+            oformat = *e;
+        }
+    }
+    if (res) {
+        std::cerr << std::endl;
+        usage();
+    } else {
+        res = pioasm.generate(oformat, input, output, options);
+    }
+    return res;
+}
\ No newline at end of file
diff --git a/tools/pioasm/output_format.h b/tools/pioasm/output_format.h
new file mode 100644
index 0000000..2ba899d
--- /dev/null
+++ b/tools/pioasm/output_format.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _OUTPUT_FORMAT_H
+#define _OUTPUT_FORMAT_H
+
+#include <vector>
+#include <map>
+#include <string>
+#include <memory>
+
+typedef unsigned int uint;
+
+// can't use optional because we want to support older compilers
+template<typename T>
+struct simple_optional {
+    T value;
+    T default_value;
+    bool specified;
+
+    simple_optional() : default_value(), specified(false) {}
+
+    simple_optional(const T &value) : value(value), specified(true) {}
+
+    simple_optional<T> &operator=(const T &v) {
+        value = v;
+        specified = true;
+        return *this;
+    }
+
+    operator bool() = delete; // confusing
+    const T &get() const { return specified ? value : default_value; }
+
+    bool is_specified() const { return specified; }
+
+    static simple_optional<T> with_default(const T &default_value) {
+        simple_optional<T> rc;
+        rc.default_value = default_value;
+        return rc;
+    }
+};
+
+typedef simple_optional<int> optional_int;
+typedef simple_optional<bool> optional_bool;
+
+struct compiled_source {
+    struct symbol {
+        std::string name;
+        int value;
+        bool is_label;
+
+        symbol(std::string name, int value, bool is_label) : name(std::move(name)), value(value), is_label(is_label) {}
+    };
+
+    struct program {
+        std::string name;
+        optional_int origin = optional_int::with_default(-1);
+        optional_int sideset_bits_including_opt;
+        bool sideset_opt = false;
+        bool sideset_pindirs = false;
+        int wrap;
+        int wrap_target;
+        std::vector<uint> instructions;
+        std::vector<symbol> symbols; // public only
+        std::map<std::string, std::vector<std::string>> code_blocks;
+        std::map<std::string, std::vector<std::pair<std::string,std::string>>> lang_opts;
+
+        // todo can't have wrap at -1
+        program(std::string name) : name(std::move(name)) {}
+    };
+
+    std::vector<symbol> global_symbols; // public only
+    std::vector<program> programs;
+};
+
+struct output_format {
+    static std::string default_name;
+
+    std::string name;
+
+    static void add(output_format *lang) {
+        all().push_back(std::shared_ptr<output_format>(lang));
+    }
+
+    virtual int output(std::string destination, std::vector<std::string> output_options,
+                       const compiled_source &source) = 0;
+
+    virtual std::string get_description() = 0;
+
+    FILE *open_single_output(std::string destination);
+    virtual ~output_format() = default;
+
+    static std::vector<std::shared_ptr<output_format>>& all() {
+        static std::vector<std::shared_ptr<output_format>> output_formats;
+        return output_formats;
+    }
+protected:
+    output_format(std::string name) : name(std::move(name)) {}
+};
+
+#endif
\ No newline at end of file
diff --git a/tools/pioasm/parser.yy b/tools/pioasm/parser.yy
new file mode 100644
index 0000000..467ca89
--- /dev/null
+++ b/tools/pioasm/parser.yy
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+%skeleton "lalr1.cc" /* -*- C++ -*- */
+%require "3.4.2"
+%defines
+
+%define api.token.constructor
+%define api.value.type variant
+/*%define parse.assert*/
+%define api.location.file "location.h"
+%define parse.lac full
+/* define parse.trace*/
+%define parse.error verbose
+%no-lines
+%locations
+
+%code requires {
+  #include <string>
+  #include <fstream>
+  #include <sstream>
+  #include "pio_types.h"
+  struct pio_assembler;
+
+  #ifdef _MSC_VER
+  #pragma warning(disable : 4065) // default only switch statement
+  #endif
+}
+
+// The parsing context.
+%param { pio_assembler& pioasm }
+
+%code {
+    #include "pio_assembler.h"
+  #ifdef _MSC_VER
+  #pragma warning(disable : 4244) // possible loss of data (valid warning, but there is a software check / missing cast)
+  #endif
+}
+
+%define api.token.prefix {TOK_}
+
+%token
+    END     0       "end of file"
+
+    NEWLINE         "end of line"
+    COMMA           ","
+    COLON           ":"
+
+    LPAREN          "("
+    RPAREN          ")"
+    LBRACKET        "["
+    RBRACKET        "]"
+    PLUS            "+"
+    MINUS           "-"
+    MULTIPLY        "*"
+    DIVIDE          "/"
+    OR              "|"
+    AND             "&"
+    XOR             "^"
+    POST_DECREMENT  "--"
+    NOT_EQUAL       "!="
+    NOT             "!"
+    REVERSE         "::"
+    EQUAL           "="
+
+    PROGRAM         ".program"
+    WRAP_TARGET     ".wrap_target"
+    WRAP            ".wrap"
+    DEFINE          ".define"
+    SIDE_SET        ".side_set"
+    WORD            ".word"
+    ORIGIN          ".origin"
+    LANG_OPT        ".lang_opt"
+
+    JMP             "jmp"
+    WAIT            "wait"
+    IN              "in"
+    OUT             "out"
+    PUSH            "push"
+    PULL            "pull"
+    MOV             "mov"
+    IRQ             "irq"
+    SET             "set"
+    NOP             "nop"
+
+    PIN             "pin"
+    GPIO            "gpio"
+    OSRE            "osre"
+
+    PINS            "pins"
+    NULL            "null"
+    PINDIRS         "pindirs"
+    BLOCK           "block"
+    NOBLOCK         "noblock"
+    IFEMPTY         "ifempty"
+    IFFULL          "iffull"
+    NOWAIT          "nowait"
+    CLEAR           "clear"
+    REL             "rel"
+    X               "x"
+    Y               "y"
+    EXEC            "exec"
+    PC              "pc"
+    ISR             "isr"
+    OSR             "osr"
+    OPTIONAL        "opt"
+    SIDE            "side"
+    STATUS          "status"
+    PUBLIC          "public"
+;
+
+%token
+    <std::string> ID "identifier"
+    <std::string> STRING "string"
+    <std::string> NON_WS "text"
+    <std::string> CODE_BLOCK_START "code block"
+    <std::string> CODE_BLOCK_CONTENTS "%}" // bit ugly but if there is no end this is what we will be missing
+    <std::string> UNKNOWN_DIRECTIVE
+    <int> INT "integer"
+;
+
+
+%left REVERSE
+%left PLUS MINUS
+%left MULTIPLY DIVIDE
+%left AND OR XOR
+
+%printer { yyo << "..."; } <*>;
+
+%%
+
+file:
+    lines END { if (pioasm.error_count || pioasm.write_output()) YYABORT; }
+    ;
+
+lines:
+  line
+  | lines NEWLINE line;
+
+line:
+    PROGRAM ID                                  { if (!pioasm.add_program(@$, $2)) { std::stringstream msg; msg << "program " << $2 << " already exists"; error(@$, msg.str()); abort(); } }
+  | directive
+  | instruction                                 { pioasm.get_current_program(@1, "instruction").add_instruction($1); }
+  | label_decl instruction                      { auto &p = pioasm.get_current_program(@2, "instruction"); p.add_label($1); p.add_instruction($2); }
+  | label_decl                                  { pioasm.get_current_program(@1, "label").add_label($1); }
+  | code_block
+  | %empty
+  | error                                       { if (pioasm.error_count > 6) { std::cerr << "\ntoo many errors; aborting.\n"; YYABORT; } }
+  ;
+
+code_block:
+  CODE_BLOCK_START CODE_BLOCK_CONTENTS          { std::string of = $1; if (of.empty()) of = output_format::default_name; pioasm.get_current_program(@$, "code block", false, false).add_code_block( code_block(@$, of, $2)); }
+
+%type <std::shared_ptr<symbol>> label_decl;
+label_decl:
+    symbol_def COLON        { $1->is_label = true; $$ = $1; }
+
+directive:
+    DEFINE symbol_def expression      { $2->is_label = false; $2->value = $3; pioasm.get_current_program(@1, ".define", false, false).add_symbol($2); }
+  | ORIGIN value                      { pioasm.get_current_program(@1, ".origin", true).set_origin(@$, $2); }
+  | SIDE_SET value OPTIONAL PINDIRS   { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, true, true); }
+  | SIDE_SET value OPTIONAL           { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, true, false); }
+  | SIDE_SET value PINDIRS            { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, false, true); }
+  | SIDE_SET value                    { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, false, false); }
+  | WRAP_TARGET                       { pioasm.get_current_program(@1, ".wrap_target").set_wrap_target(@$); }
+  | WRAP                              { pioasm.get_current_program(@1, ".wrap").set_wrap(@$); }
+  | WORD value                        { pioasm.get_current_program(@1, "instruction").add_instruction(std::shared_ptr<instruction>(new instr_word(@$, $2))); }
+  | LANG_OPT NON_WS NON_WS EQUAL INT  { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, std::to_string($5)); }
+  | LANG_OPT NON_WS NON_WS EQUAL STRING { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, $5); }
+  | LANG_OPT NON_WS NON_WS EQUAL NON_WS { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, $5); }
+  | LANG_OPT error                    { error(@$, "expected format is .lang_opt language option_name = option_value"); }
+  | UNKNOWN_DIRECTIVE                 { std::stringstream msg; msg << "unknown directive " << $1; throw syntax_error(@$, msg.str()); }
+  ;
+
+/* value is a more limited top level expression... requiring parenthesis */
+%type <std::shared_ptr<resolvable>> value;
+value: INT { $$ = resolvable_int(@$, $1); }
+     | ID { $$ = std::shared_ptr<resolvable>(new name_ref(@$, $1)); }
+     | LPAREN expression RPAREN { $$ = $2; }
+
+%type <std::shared_ptr<resolvable>> expression;
+expression:
+     value
+     | expression PLUS expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::add, $1, $3)); }
+     | expression MINUS expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::subtract, $1, $3)); }
+     | expression MULTIPLY expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::multiply, $1, $3));  }
+     | expression DIVIDE expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::divide, $1, $3)); }
+     | expression OR expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::or_, $1, $3)); }
+     | expression AND expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::and_, $1, $3)); }
+     | expression XOR expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::xor_, $1, $3)); }
+     | MINUS expression { $$ = std::shared_ptr<unary_operation>(new unary_operation(@$, unary_operation::negate, $2)); }
+     | REVERSE expression { $$ = std::shared_ptr<unary_operation>(new unary_operation(@$, unary_operation::reverse, $2)); }
+
+%type <std::shared_ptr<instruction>> instruction;
+instruction:
+    base_instruction sideset delay { $$ = $1; $$->sideset = $2; $$->delay = $3; }
+  | base_instruction delay sideset { $$ = $1; $$->delay = $2; $$->sideset = $3; }
+  | base_instruction sideset { $$ = $1; $$->sideset = $2; $$->delay = resolvable_int(@$, 0); }
+  | base_instruction delay { $$ = $1; $$->delay = $2; }
+  | base_instruction { $$ = $1; $$->delay = resolvable_int(@$, 0); }
+
+%type <std::shared_ptr<instruction>> base_instruction;
+base_instruction:
+    NOP                                                   { $$ = std::shared_ptr<instruction>(new instr_nop(@$)); }
+    | JMP condition comma expression                      { $$ = std::shared_ptr<instruction>(new instr_jmp(@$, $2, $4)); }
+    | WAIT value wait_source                              { $$ = std::shared_ptr<instruction>(new instr_wait(@$, $2, $3)); }
+    | WAIT value COMMA value                              { std::stringstream msg; location l; l.begin = @2.end; l.end = @3.end; msg << "expected irq, gpio or pin after the polarity value and before the \",\""; throw yy::parser::syntax_error(l, msg.str()); }
+    | WAIT wait_source                                    { $$ = std::shared_ptr<instruction>(new instr_wait(@$, resolvable_int(@$, 1),  $2)); }
+    | IN in_source comma value                            { $$ = std::shared_ptr<instruction>(new instr_in(@$, $2, $4)); }
+    | OUT out_target comma value                          { $$ = std::shared_ptr<instruction>(new instr_out(@$, $2, $4)); }
+    | PUSH if_full blocking                               { $$ = std::shared_ptr<instruction>(new instr_push(@$, $2, $3)); }
+    | PULL if_empty blocking                              { $$ = std::shared_ptr<instruction>(new instr_pull(@$, $2, $3)); }
+    | MOV mov_target comma mov_op mov_source              { $$ = std::shared_ptr<instruction>(new instr_mov(@$, $2, $5, $4)); }
+    | IRQ irq_modifiers value REL                         { $$ = std::shared_ptr<instruction>(new instr_irq(@$, $2, $3, true)); }
+    | IRQ irq_modifiers value                             { $$ = std::shared_ptr<instruction>(new instr_irq(@$, $2, $3)); }
+    | SET set_target comma value                          { $$ = std::shared_ptr<instruction>(new instr_set(@$, $2, $4)); }
+;
+
+%type <std::shared_ptr<resolvable>> delay;
+delay:
+    LBRACKET expression RBRACKET { $$ = $2; }
+
+%type <std::shared_ptr<resolvable>> sideset;
+sideset:
+    SIDE value { $$ = $2; }
+
+%type <enum condition> condition;
+condition:
+    NOT X                   { $$ = condition::xz; }
+  | X POST_DECREMENT        { $$ = condition::xnz__; }
+  | NOT Y                   { $$ = condition::yz; }
+  | Y POST_DECREMENT        { $$ = condition::ynz__; }
+  | X NOT_EQUAL Y           { $$ = condition::xney; }
+  | PIN                     { $$ = condition::pin; }
+  | NOT OSRE                { $$ = condition::osrez; }
+  | %empty                  { $$ = condition::al; }
+
+%type <std::shared_ptr<wait_source>> wait_source;
+wait_source:
+    IRQ comma value REL     { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, $3, true)); }
+  | IRQ comma value         { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, $3, false)); }
+  | GPIO comma value        { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::gpio, $3)); }
+  | PIN comma value         { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::pin, $3)); }
+
+comma: COMMA | %empty        /* not a huge fan of forcing commas */
+
+%type <enum in_out_set> in_source;
+in_source: PINS { $$ = in_out_set::in_out_set_pins; }
+    | X         { $$ = in_out_set::in_out_set_x; }
+    | Y         { $$ = in_out_set::in_out_set_y; }
+    | NULL      { $$ = in_out_set::in_out_null; }
+    | ISR       { $$ = in_out_set::in_out_isr; }
+    | OSR       { $$ = in_out_set::in_osr; }
+    | STATUS    { $$ = in_out_set::in_status; }
+
+%type <enum in_out_set> out_target;
+out_target: PINS { $$ = in_out_set::in_out_set_pins; }
+    | X          { $$ = in_out_set::in_out_set_x; }
+    | Y          { $$ = in_out_set::in_out_set_y; }
+    | NULL       { $$ = in_out_set::in_out_null; }
+    | PINDIRS    { $$ = in_out_set::in_out_set_pindirs; }
+    | ISR        { $$ = in_out_set::in_out_isr; }
+    | PC         { $$ = in_out_set::out_set_pc; }
+    | EXEC       { $$ = in_out_set::out_exec; }
+
+%type <enum mov> mov_target;
+mov_target: PINS { $$ = mov::pins; }
+    | X          { $$ = mov::x; }
+    | Y          { $$ = mov::y; }
+    | EXEC       { $$ = mov::exec; }
+    | PC         { $$ = mov::pc; }
+    | ISR        { $$ = mov::isr; }
+    | OSR        { $$ = mov::osr; }
+
+%type <enum mov> mov_source;
+mov_source: PINS { $$ = mov::pins; }
+    | X          { $$ = mov::x; }
+    | Y          { $$ = mov::y; }
+    | NULL       { $$ = mov::null; }
+    | STATUS     { $$ = mov::status; }
+    | ISR        { $$ = mov::isr; }
+    | OSR        { $$ = mov::osr; }
+
+%type <enum mov_op> mov_op;
+mov_op:
+    NOT         { $$ = mov_op::invert; }
+  | REVERSE     { $$ = mov_op::bit_reverse; }
+  | %empty      { $$ = mov_op::none; }
+
+%type <enum in_out_set> set_target;
+set_target:
+    PINS        { $$ = in_out_set::in_out_set_pins; }
+  | X           { $$ = in_out_set::in_out_set_x; }
+  | Y           { $$ = in_out_set::in_out_set_y; }
+  | PINDIRS     { $$ = in_out_set::in_out_set_pindirs; }
+
+%type <bool> if_full;
+if_full:
+    IFFULL { $$ = true; }
+  | %empty { $$ = false; }
+
+%type <bool> if_empty;
+if_empty:
+    IFEMPTY { $$ = true; }
+  | %empty  { $$ = false; }
+
+%type <bool> blocking;
+blocking:
+    BLOCK   { $$ = true; }
+  | NOBLOCK { $$ = false; }
+  | %empty  { $$ = true; }
+
+%type <enum irq> irq_modifiers;
+irq_modifiers:
+    CLEAR          { $$ = irq::clear; }
+  | WAIT           { $$ = irq::set_wait; }
+  | NOWAIT         { $$ = irq::set; }
+  | SET            { $$ = irq::set; }
+  | %empty         { $$ = irq::set; }
+
+%type <std::shared_ptr<symbol>> symbol_def;
+symbol_def:
+    ID              { $$ = std::shared_ptr<symbol>(new symbol(@$, $1)); }
+  | PUBLIC ID       { $$ = std::shared_ptr<symbol>(new symbol(@$, $2, true)); }
+  | MULTIPLY ID     { $$ = std::shared_ptr<symbol>(new symbol(@$, $2, true)); }
+
+%%
+void yy::parser::error(const location_type& l, const std::string& m)
+{
+   if (l.begin.filename) {
+      std::cerr << l << ": " << m << '\n';
+      pioasm.error_count++;
+      if (l.begin.line == l.end.line && *l.begin.filename == *l.end.filename) {
+        std::ifstream file(l.begin.filename->c_str());
+        std::string line;
+        for(int i = 0; i < l.begin.line; ++i) {
+             std::getline(file, line);
+        }
+        fprintf(stderr, "%5d | %s\n", l.begin.line, line.c_str());
+        fprintf(stderr, "%5s | %*s", "", l.begin.column, "^");
+        for (int i = l.begin.column; i < l.end.column - 1; i++) {
+              putc ('~', stderr);
+        }
+        putc ('\n', stderr);
+      }
+  } else {
+      std::cerr << m << '\n';
+  }
+}
+
diff --git a/tools/pioasm/pio_assembler.cpp b/tools/pioasm/pio_assembler.cpp
new file mode 100644
index 0000000..469d733
--- /dev/null
+++ b/tools/pioasm/pio_assembler.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstdio>
+#include <iterator>
+#include "pio_assembler.h"
+#include "parser.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4996) // fopen
+#endif
+
+using syntax_error = yy::parser::syntax_error;
+
+std::string output_format::default_name = "c-sdk";
+
+pio_assembler::pio_assembler() {
+}
+
+int pio_assembler::generate(std::shared_ptr<output_format> _format, const std::string &_source,
+                            const std::string &_dest, const std::vector<std::string> &_options) {
+    format = _format;
+    source = _source;
+    dest = _dest;
+    options = _options;
+    location.initialize(&source);
+    scan_begin();
+    yy::parser parse(*this);
+//    parse.set_debug_level(false);
+    int res = parse();
+    scan_end();
+    return res;
+}
+
+void program::add_instruction(std::shared_ptr<instruction> inst) {
+    uint limit = MAX_INSTRUCTIONS;
+    if (instructions.size() >= limit) {
+        // todo take offset into account
+        std::stringstream msg;
+        msg << "program instruction limit of " << limit << " instruction(s) exceeded";
+        throw syntax_error(inst->location, msg.str());
+    }
+    if (!sideset_opt && !inst->sideset) {
+        std::stringstream msg;
+        msg << "instruction requires 'side' to specify side set value for the instruction because non optional sideset was specified for the program at " << sideset.location;
+        throw syntax_error(inst->location, msg.str());
+    }
+    instructions.push_back(inst);
+}
+
+using syntax_error = yy::parser::syntax_error;
+
+void program::add_symbol(std::shared_ptr<symbol> symbol) {
+    const auto &existing = pioasm->get_symbol(symbol->name, this);
+    if (existing) {
+        std::stringstream msg;
+        if (symbol->is_label != existing->is_label) {
+            msg << "'" << symbol->name << "' was already defined as a " << (existing->is_label ? "label" : "value")
+                << " at " << existing->location;
+        } else if (symbol->is_label) {
+            msg << "label '" << symbol->name << "' was already defined at " << existing->location;
+        } else {
+            msg << "'" << symbol->name << "' was already defined at " << existing->location;
+        }
+        throw syntax_error(symbol->location, msg.str());
+    }
+    symbols.insert(std::pair<std::string, std::shared_ptr<::symbol>>(symbol->name, symbol));
+    ordered_symbols.push_back(symbol);
+}
+
+int resolvable::resolve(const program &program) {
+    return resolve(program.pioasm, &program);
+}
+
+int unary_operation::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
+    int value = arg->resolve(pioasm, program, scope);
+    switch (op) {
+        case negate:
+            return -value;
+        case reverse: {
+            // slow is fine
+            uint result = 0;
+            for (uint i = 0; i < 32; i++) {
+                result <<= 1u;
+                if (value & 1u) {
+                    result |= 1u;
+                }
+                value >>= 1u;
+            }
+            return result;
+        }
+        default:
+            throw syntax_error(location, "internal error");
+    }
+}
+
+int binary_operation::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
+    int lvalue = left->resolve(pioasm, program, scope);
+    int rvalue = right->resolve(pioasm, program, scope);
+    switch (op) {
+        case add:
+            return lvalue + rvalue;
+        case subtract:
+            return lvalue - rvalue;
+        case multiply:
+            return lvalue * rvalue;
+        case divide:
+            return lvalue / rvalue;
+        case and_:
+            return lvalue & rvalue;
+        case or_:
+            return lvalue | rvalue;
+        case xor_:
+            return lvalue ^ rvalue;
+        default:
+            throw syntax_error(location, "internal error");
+    }
+}
+
+void program::set_wrap(const yy::location &l) {
+    if (wrap) {
+        std::stringstream msg;
+        msg << ".wrap was already specified at " << wrap->location;
+        throw syntax_error(l, msg.str());
+    }
+    if (instructions.empty()) {
+        throw syntax_error(l, ".wrap cannot be placed before the first program instruction");
+    }
+    wrap = resolvable_int(l, instructions.size() - 1);
+}
+
+void program::set_wrap_target(const yy::location &l) {
+    if (wrap_target) {
+        std::stringstream msg;
+        msg << ".wrap_target was already specified at " << wrap_target->location;
+        throw syntax_error(l, msg.str());
+    }
+    wrap_target = resolvable_int(l, instructions.size());
+}
+
+void program::add_code_block(const code_block &block) {
+    code_blocks[block.lang].push_back(block);
+}
+
+void program::add_lang_opt(std::string lang, std::string name, std::string value) {
+    lang_opts[lang].emplace_back(name, value);
+}
+
+void program::finalize() {
+    if (sideset.value) {
+        int bits = sideset.value->resolve(*this);
+        if (bits < 0) {
+            throw syntax_error(sideset.value->location, "number of side set bits must be positive");
+        }
+        sideset_max = (1u << bits) - 1;
+        if (sideset_opt) bits++;
+        sideset_bits_including_opt = bits;
+        if (bits > 5) {
+            if (sideset_opt)
+                throw syntax_error(sideset.value->location, "maximum number of side set bits with optional is 4");
+            else
+                throw syntax_error(sideset.value->location, "maximum number of side set bits is 5");
+        }
+        delay_max = (1u << (5 - bits)) - 1;
+    } else {
+        sideset_max = 0;
+        delay_max = 31;
+    }
+}
+
+int name_ref::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
+    auto symbol = pioasm->get_symbol(name, program);
+    if (symbol) {
+        if (symbol->resolve_started) {
+            std::stringstream msg;
+            msg << "circular dependency in definition of '" << name << "'; detected at " << location << ")";
+            throw syntax_error(scope.location, msg.str());
+        }
+        try {
+            symbol->resolve_started++;
+            int rc = symbol->value->resolve(pioasm, program, scope);
+            symbol->resolve_started--;
+            return rc;
+        } catch (syntax_error &e) {
+            symbol->resolve_started--;
+            throw e;
+        }
+    } else {
+        std::stringstream msg;
+        msg << "undefined symbol '" << name << "'";
+        throw syntax_error(location, msg.str());
+    }
+}
+
+uint instruction::encode(const program &program) {
+    raw_encoding raw = raw_encode(program);
+    int _delay = delay->resolve(program);
+    if (_delay < 0) {
+        throw syntax_error(delay->location, "instruction delay must be positive");
+    }
+    if (_delay > program.delay_max) {
+        if (program.delay_max == 31) {
+            throw syntax_error(delay->location, "instruction delay must be <= 31");
+        } else {
+            std::stringstream msg;
+            msg << "the instruction delay limit is " << program.delay_max << " because of the side set specified at "
+                << program.sideset.location;
+            throw syntax_error(delay->location, msg.str());
+        }
+    }
+    int _sideset = 0;
+    if (sideset) {
+        _sideset = sideset->resolve(program);
+        if (_sideset < 0) {
+            throw syntax_error(sideset->location, "side set value must be >=0");
+        }
+        if (_sideset > program.sideset_max) {
+            std::stringstream msg;
+            msg << "the maximum side set value is " << program.sideset_max << " based on the configuration specified at "
+                << program.sideset.location;
+            throw syntax_error(sideset->location, msg.str());
+        }
+        _sideset <<= (5u - program.sideset_bits_including_opt);
+        if (program.sideset_opt) {
+            _sideset |= 0x10u;
+        }
+    }
+    return (((uint) raw.type) << 13u) | (((uint) _delay | (uint) _sideset) << 8u) | (raw.arg1 << 5u) | raw.arg2;
+}
+
+raw_encoding instruction::raw_encode(const program &program) {
+    throw syntax_error(location, "internal error");
+}
+
+uint instr_word::encode(const program &program) {
+    uint value = encoding->resolve(program);
+    if (value > 0xffffu) {
+        throw syntax_error(location, ".word value must be a positive 16 bit value");
+    }
+    return value;
+}
+
+raw_encoding instr_jmp::raw_encode(const program &program) {
+    int dest = target->resolve(program);
+    if (dest < 0) {
+        throw syntax_error(target->location, "jmp target address must be positive");
+    } else if (dest >= (int)program.instructions.size()) {
+        std::stringstream msg;
+        msg << "jmp target address " << dest << " is beyond the end of the program";
+        throw syntax_error(target->location, msg.str());
+    }
+    return {inst_type::jmp, (uint) cond, (uint) dest};
+}
+
+raw_encoding instr_in::raw_encode(const program &program) {
+    int v = value->resolve(program);
+    if (v < 1 || v > 32) {
+        throw syntax_error(value->location, "'in' bit count must be >= 1 and <= 32");
+    }
+    return {inst_type::in, (uint) src, (uint) v & 0x1fu};
+}
+
+raw_encoding instr_out::raw_encode(const program &program) {
+    int v = value->resolve(program);
+    if (v < 1 || v > 32) {
+        throw syntax_error(value->location, "'out' bit count must be >= 1 and <= 32");
+    }
+    return {inst_type::out, (uint) dest, (uint) v & 0x1fu};
+}
+
+raw_encoding instr_set::raw_encode(const program &program) {
+    int v = value->resolve(program);
+    if (v < 0 || v > 31) {
+        throw syntax_error(value->location, "'set' bit count must be >= 0 and <= 31");
+    }
+    return {inst_type::set, (uint) dest, (uint) v};
+}
+
+raw_encoding instr_wait::raw_encode(const program &program) {
+    uint pol = polarity->resolve(program);
+    if (pol > 1) {
+        throw syntax_error(polarity->location, "'wait' polarity must be 0 or 1");
+    }
+    uint arg2 = source->param->resolve(program);
+    switch (source->target) {
+        case wait_source::irq:
+            if (arg2 > 7) throw syntax_error(source->param->location, "irq number must be must be >= 0 and <= 7");
+            break;
+        case wait_source::gpio:
+            if (arg2 > 31)
+                throw syntax_error(source->param->location, "absolute GPIO number must be must be >= 0 and <= 31");
+            break;
+        case wait_source::pin:
+            if (arg2 > 31) throw syntax_error(polarity->location, "pin number must be must be >= 0 and <= 31");
+            break;
+    }
+    return {inst_type::wait, (pol << 2u) | (uint) source->target, arg2 | (source->flag ? 0x10u : 0u)};
+}
+
+raw_encoding instr_irq::raw_encode(const program &program) {
+    uint arg2 = num->resolve(program);
+    if (arg2 > 7) throw syntax_error(num->location, "irq number must be must be >= 0 and <= 7");
+    if (relative) arg2 |= 0x10u;
+    return {inst_type::irq, (uint)modifiers, arg2};
+}
+
+std::vector<compiled_source::symbol> pio_assembler::public_symbols(program &program) {
+    std::vector<std::shared_ptr<symbol>> public_symbols;
+    std::remove_copy_if(program.ordered_symbols.begin(), program.ordered_symbols.end(),
+                        std::inserter(public_symbols, public_symbols.end()),
+                        [](const std::shared_ptr<symbol> &s) { return !s->is_public; });
+
+    std::vector<compiled_source::symbol> rc;
+    std::transform(public_symbols.begin(), public_symbols.end(), std::back_inserter(rc),
+                   [&](const std::shared_ptr<symbol> &s) {
+                       return compiled_source::symbol(s->name, s->value->resolve(program), s->is_label);
+                   });
+    return rc;
+}
+
+int pio_assembler::write_output() {
+    std::set<std::string> known_output_formats;
+    std::transform(output_format::all().begin(), output_format::all().end(),
+                   std::inserter(known_output_formats, known_output_formats.begin()),
+                   [&](std::shared_ptr<output_format> &f) {
+                       return f->name;
+                   });
+
+    compiled_source source;
+    source.global_symbols = public_symbols(get_dummy_global_program());
+    for (auto &program : programs) {
+        program.finalize();
+        source.programs.emplace_back(compiled_source::program(program.name));
+        auto &cprogram = source.programs[source.programs.size() - 1];
+        cprogram = compiled_source::program(program.name);
+
+        // encode the instructions
+        std::transform(program.instructions.begin(), program.instructions.end(),
+                       std::back_inserter(cprogram.instructions), [&](std::shared_ptr<instruction> &inst) {
+                    return inst->encode(program);
+                });
+
+        for (const auto &e : program.code_blocks) {
+            bool ok = false;
+            for(const auto &o : known_output_formats) {
+                if (o == e.first || 0 == e.first.find(o+"-")) {
+                    ok = true;
+                    break;
+                }
+            }
+            if (!ok) {
+                std::cerr << e.second[0].location << ": warning, unknown code block output type '" << e.first << "'\n";
+                known_output_formats.insert(e.first);
+            }
+        }
+
+        if (program.wrap) cprogram.wrap = program.wrap->resolve(program); else cprogram.wrap = std::max((int)program.instructions.size() - 1, 0);
+        if (program.wrap_target) cprogram.wrap_target = program.wrap_target->resolve(program); else cprogram.wrap_target = 0;
+        if (program.origin.value) cprogram.origin = program.origin.value->resolve(program);
+        if (program.sideset.value) {
+            cprogram.sideset_bits_including_opt = program.sideset_bits_including_opt;
+            cprogram.sideset_opt = program.sideset_opt;
+            cprogram.sideset_pindirs = program.sideset_pindirs;
+        }
+        std::transform(program.code_blocks.begin(), program.code_blocks.end(), std::inserter(cprogram.code_blocks, cprogram.code_blocks.begin()), [](const std::pair<std::string, std::vector<code_block>>&e) {
+            std::vector<std::string> blocks;
+            std::transform(e.second.begin(), e.second.end(), std::back_inserter(blocks), [&](const code_block& block) {
+                return block.contents;
+            });
+            return std::pair<std::string, std::vector<std::string>>(e.first, blocks);
+        });
+        cprogram.lang_opts = program.lang_opts;
+        cprogram.symbols = public_symbols(program);
+    }
+    if (programs.empty()) {
+        std::cout << "warning: input contained no programs" << std::endl;
+    }
+    return format->output(dest, options, source);
+}
+
+FILE *output_format::open_single_output(std::string destination) {
+    FILE *out = destination == "-" ? stdout : fopen(destination.c_str(), "w");
+    if (!out) {
+        std::cerr << "Can't open output file '" << destination << "'" << std::endl;
+    }
+    return out;
+}
diff --git a/tools/pioasm/pio_assembler.h b/tools/pioasm/pio_assembler.h
new file mode 100644
index 0000000..7183800
--- /dev/null
+++ b/tools/pioasm/pio_assembler.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _PIO_ASSEMBLER_H
+#define _PIO_ASSEMBLER_H
+
+#include <algorithm>
+#include "parser.hpp"
+#include "output_format.h"
+
+// Give Flex the prototype of yylex we want ...
+# define YY_DECL \
+  yy::parser::symbol_type yylex (pio_assembler& pioasm)
+// ... and declare it for the parser's sake.
+YY_DECL;
+
+
+struct pio_assembler {
+public:
+    using syntax_error = yy::parser::syntax_error;
+    using location_type = yy::parser::location_type;
+
+    std::shared_ptr<program> dummy_global_program;
+    std::vector<program> programs;
+    int error_count = 0;
+
+    pio_assembler();
+
+    std::shared_ptr<output_format> format;
+    // The name of the file being parsed.
+    std::string source;
+    // name of the output file or "-" for stdout
+    std::string dest;
+    std::vector<std::string> options;
+
+    int write_output();
+
+    bool add_program(const yy::location &l, const std::string &name) {
+        if (std::find_if(programs.begin(), programs.end(), [&](const program &p) { return p.name == name; }) ==
+            programs.end()) {
+            programs.emplace_back(this, l, name);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    program &get_dummy_global_program() {
+        if (!dummy_global_program) {
+            dummy_global_program = std::shared_ptr<program>(new program(this, yy::location(&source), ""));
+        }
+        return *dummy_global_program;
+    }
+
+    program &get_current_program(const location_type &l, const std::string &requiring_program,
+                                 bool before_any_instructions = false, bool disallow_global = true) {
+        if (programs.empty()) {
+            if (disallow_global) {
+                std::stringstream msg;
+                msg << requiring_program << " is invalid outside of a program";
+                throw syntax_error(l, msg.str());
+            }
+            return get_dummy_global_program();
+        }
+        auto &p = programs[programs.size() - 1];
+        if (before_any_instructions && !p.instructions.empty()) {
+            std::stringstream msg;
+            msg << requiring_program << " must preceed any program instructions";
+            throw syntax_error(l, msg.str());
+        }
+        return p;
+    }
+
+    // note p may be null for global symbols only
+    std::shared_ptr<symbol> get_symbol(const std::string &name, const program *p) {
+        const auto &i = get_dummy_global_program().symbols.find(name);
+        if (i != get_dummy_global_program().symbols.end())
+            return i->second;
+
+        if (p) {
+            const auto &i2 = p->symbols.find(name);
+            if (i2 != p->symbols.end())
+                return i2->second;
+        }
+        return nullptr;
+    }
+
+    std::vector<compiled_source::symbol> public_symbols(program &program);
+    int generate(std::shared_ptr<output_format> _format, const std::string &_source, const std::string &_dest,
+                 const std::vector<std::string> &_options = std::vector<std::string>());
+
+    // Handling the scanner.
+    void scan_begin();
+    void scan_end();
+
+    // The token's location used by the scanner.
+    yy::location location;
+};
+
+#endif
\ No newline at end of file
diff --git a/tools/pioasm/pio_disassembler.cpp b/tools/pioasm/pio_disassembler.cpp
new file mode 100644
index 0000000..c30fb0b
--- /dev/null
+++ b/tools/pioasm/pio_disassembler.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <array>
+#include <sstream>
+#include <iomanip>
+#include "pio_disassembler.h"
+
+extern "C" void disassemble(char *buf, int buf_len, uint16_t inst, uint sideset_bits, bool sideset_opt) {
+    if (buf_len) buf[disassemble(inst, sideset_bits, sideset_opt).copy(buf, buf_len - 1)] = 0;
+}
+
+std::string disassemble(uint16_t inst, uint sideset_bits_including_opt, bool sideset_opt) {
+    std::stringstream ss;
+    uint major = inst >> 13u;
+    uint arg1 = ((uint) inst >> 5u) & 0x7u;
+    uint arg2 = inst & 0x1fu;
+    auto op = [&](const std::string &s) {
+        ss << std::left << std::setw(7) << s;
+    };
+    auto op_guts = [&](const std::string &s) {
+        ss << std::left << std::setw(16) << s;
+    };
+
+    bool invalid = false;
+    switch (major) {
+        case 0b000: {
+            static std::array<std::string, 8> conditions{"", "!x, ", "x--, ", "!y, ", "y--, ", "x != y, ", "pin, ",
+                                                         "!osre, "};
+            op("jmp");
+            op_guts(conditions[arg1] + std::to_string(arg2));
+            break;
+        }
+        case 0b001: {
+            uint source = arg1 & 3u;
+            std::string guts;
+            switch (source) {
+                case 0b00:
+                    guts = "gpio, " + std::to_string(arg2);
+                    break;
+                case 0b01:
+                    guts = "pin, " + std::to_string(arg2);
+                    break;
+                case 0b10:
+                    if (arg2 & 0x8u) {
+                        invalid = true;
+                    } else {
+                        guts = "irq, " + std::to_string(arg2 & 7u);
+                        if (arg2 & 0x10u) {
+                            guts += " rel";
+                        }
+                    }
+                    break;
+            }
+            if (!invalid) {
+                guts = ((arg1 & 4u) ? "1 " : "0 ") + guts;
+                op("wait");
+                op_guts(guts);
+            }
+            break;
+        }
+        case 0b010: {
+            static std::array<std::string, 8> sources { "pins", "x", "y", "null", "", "status", "isr", "osr"};
+            std::string source = sources[arg1];
+            if (source.empty()) {
+                invalid = true;
+            } else {
+                op("in");
+                op_guts(source + ", " + std::to_string(arg2 ? arg2 : 32));
+            }
+            break;
+        }
+        case 0b011: {
+            static std::array<std::string, 8> dests { "pins", "x", "y", "null", "pindirs", "pc", "isr", "exec"};
+            op("out");
+            op_guts(dests[arg1] + ", " + std::to_string(arg2 ? arg2 : 32));
+            break;
+        }
+        case 0b100: {
+            if (arg2) {
+                invalid = true;
+            } else {
+                std::string guts = "";
+                if (arg1 & 4u) {
+                    op("pull");
+                    if (arg1 & 2u) guts = "ifempty ";
+                } else {
+                    op("push");
+                    if (arg1 & 2u) guts = "iffull ";
+                }
+                guts += (arg1 & 0x1u) ? "block" : "noblock";
+                op_guts(guts);
+            }
+            break;
+        }
+        case 0b101: {
+            static std::array<std::string, 8> dests { "pins", "x", "y", "", "exec", "pc", "isr", "osr"};
+            static std::array<std::string, 8> sources { "pins", "x", "y", "null", "", "status", "isr", "osr"};
+            std::string dest = dests[arg1];
+            std::string source = sources[arg2 & 7u];
+            uint operation = arg2 >> 3u;
+            if (source.empty() || dest.empty() || operation == 3) {
+                invalid = true;
+            }
+            if (dest == source && !operation && (arg1 == 1 || arg2 == 2)) {
+                op("nop");
+                op_guts("");
+            } else {
+                op("mov");
+                std::string guts = dest + ", ";
+                if (operation == 1) {
+                    guts += "!";
+                } else if (operation == 2) {
+                    guts += "::";
+                }
+                guts += source;
+                op_guts(guts);
+            }
+            break;
+        }
+        case 0b110: {
+            if ((arg1 & 0x4u) || (arg2 & 0x8u)) {
+                invalid = true;
+            } else {
+                op("irq");
+                std::string guts;
+                if (arg1 & 0x2u) {
+                    guts += "clear ";
+                } else if (arg1 & 0x1u) {
+                    guts += "wait ";
+                } else {
+                    guts += "nowait ";
+                }
+                guts += std::to_string(arg2 & 7u);
+                if (arg2 & 0x10u) {
+                    guts += " rel";
+                }
+                op_guts(guts);
+            }
+            break;
+        }
+        case 0b111: {
+            static std::array<std::string, 8> dests{"pins", "x", "y", "", "pindirs", "", "", ""};
+            std::string dest = dests[arg1];
+            if (dest.empty()) {
+                invalid = true;
+            } else {
+                op("set");
+                op_guts(dests[arg1] + ", " + std::to_string(arg2));
+            }
+            break;
+        }
+    }
+    if (invalid) {
+        return "reserved";
+    }
+    uint delay = ((uint) inst >> 8u) & 0x1f;
+    ss << std::left << std::setw(7);
+    if (sideset_bits_including_opt && (!sideset_opt || (delay & 0x10u))) {
+        ss << ("side "+ std::to_string((delay & (sideset_opt ? 0xfu : 0x1fu)) >> (5u - sideset_bits_including_opt)));
+    } else {
+        ss << "";
+    }
+    delay &= ((1u << (5 - sideset_bits_including_opt)) - 1u);
+    ss << std::left << std::setw(4) << (delay ? ("[" + std::to_string(delay) + "]") : "");
+    return ss.str();
+}
+
diff --git a/tools/pioasm/pio_disassembler.h b/tools/pioasm/pio_disassembler.h
new file mode 100644
index 0000000..669e08d
--- /dev/null
+++ b/tools/pioasm/pio_disassembler.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _PIO_DISASSEMBLER_H
+#define _PIO_DISASSEMBLER_H
+
+#ifdef __cplusplus
+
+#include <string>
+
+typedef unsigned int uint;
+
+std::string disassemble(uint16_t inst, uint sideset_bits, bool sideset_opt);
+extern "C" void disassemble(char *buf, int buf_len, uint16_t inst, uint sideset_bits, bool sideset_opt);
+#else
+void disassemble(char *buf, int buf_len, uint inst, uint sideset_bits, bool sideset_opt);
+#endif
+
+#endif
\ No newline at end of file
diff --git a/tools/pioasm/pio_types.h b/tools/pioasm/pio_types.h
new file mode 100644
index 0000000..d262849
--- /dev/null
+++ b/tools/pioasm/pio_types.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _PIO_TYPES_H
+#define _PIO_TYPES_H
+
+#include <string>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+#include <memory>
+
+#include "location.h"
+
+typedef unsigned int uint;
+
+enum struct inst_type {
+    jmp = 0x0,
+    wait = 0x1,
+    in = 0x2,
+    out = 0x3,
+    push_pull = 0x4,
+    mov = 0x5,
+    irq = 0x6,
+    set = 0x7,
+};
+
+/* condition codes */
+enum struct condition {
+    al = 0x0,
+    xz = 0x1,
+    xnz__ = 0x2,
+    yz = 0x3,
+    ynz__ = 0x4,
+    xney = 0x5,
+    pin = 0x6,
+    osrez = 0x7,
+};
+
+/* in source / out / set target - not all valid */
+enum struct in_out_set {
+    in_out_set_pins = 0x0,
+    in_out_set_x = 0x1,
+    in_out_set_y = 0x2,
+    in_out_null = 0x3,
+    in_out_set_pindirs = 0x4,
+    in_status = 0x5,
+    out_set_pc = 0x5,
+    in_out_isr = 0x6,
+    in_osr = 0x7,
+    out_exec = 0x7,
+};
+
+enum struct irq {
+    set = 0x0,
+    set_wait = 0x1,
+    clear = 0x2,
+};
+
+// mov src/dest (not all valid)
+enum struct mov {
+    pins = 0x0,
+    x = 0x1,
+    y = 0x2,
+    null = 0x3,
+    exec = 0x4,
+    pc = 0x5,
+    status = 0x5,
+    isr = 0x6,
+    osr = 0x7,
+};
+
+enum struct mov_op {
+    none = 0x0,
+    invert = 0x1,
+    bit_reverse = 0x2,
+};
+
+struct src_item {
+    yy::location location;
+
+    src_item() = default;
+
+    explicit src_item(const yy::location &location) : location(location) {}
+};
+
+struct program;
+struct pio_assembler;
+
+struct resolvable : public src_item {
+    resolvable(const yy::location &l) : src_item(l) {}
+
+    int resolve(const program &program);
+
+    int resolve(pio_assembler *pioasm, const program *program) {
+        return resolve(pioasm, program, *this);
+    }
+
+    virtual int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) = 0;
+};
+
+using rvalue = std::shared_ptr<resolvable>;
+
+struct wait_source {
+    enum type {
+        gpio = 0x0,
+        pin = 0x1,
+        irq = 0x2
+    } target;
+    rvalue param;
+    bool flag;
+
+    wait_source(type target, rvalue param, bool flag = false) : target(target), param(std::move(param)), flag(flag) {}
+};
+
+struct name_ref : public resolvable {
+    std::string name;
+
+    name_ref(const yy::location &l, std::string name) : resolvable(l), name(std::move(name)) {}
+
+    int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) override;
+};
+
+struct code_block : public resolvable {
+    std::string lang;
+    std::string contents;
+
+    code_block(const yy::location &l, std::string lang, std::string contents) : resolvable(l), lang(std::move(lang)),
+                                                                                contents(std::move(contents)) {}
+
+    int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) override {
+        return 0;
+    }
+};
+
+struct int_value : public resolvable {
+    int value;
+
+    int_value(const yy::location &l, int value) : resolvable(l), value(value) {}
+
+    int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) override {
+        return value;
+    }
+};
+
+static inline rvalue resolvable_int(const yy::location &l, int v) {
+    return std::shared_ptr<resolvable>(new int_value(l, v));
+}
+
+struct binary_operation : public resolvable {
+    enum op_type {
+        add,
+        subtract,
+        multiply,
+        divide,
+        and_, // pesky C++
+        or_,
+        xor_
+    };
+
+    op_type op;
+    rvalue left, right;
+
+    binary_operation(const yy::location &l, op_type op, rvalue left, rvalue right) :
+            resolvable(l), op(op), left(std::move(left)), right(std::move(right)) {}
+
+    int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) override;
+};
+
+struct unary_operation : public resolvable {
+    enum op_type {
+        negate,
+        reverse
+    };
+
+    op_type op;
+    rvalue arg;
+
+    unary_operation(const yy::location &l, op_type op, const rvalue &arg) :
+            resolvable(l), op(op), arg(arg) {}
+
+    int resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) override;
+};
+
+struct symbol : public src_item {
+    std::string name;
+    rvalue value;
+    bool is_public;
+    bool is_label;
+    int resolve_started;
+
+    symbol(const yy::location &l, std::string name, bool is_extern = false) : src_item(l), name(std::move(name)),
+                                                                              is_public(is_extern), is_label(false),
+                                                                              resolve_started(false) {}
+};
+
+struct raw_encoding {
+    enum inst_type type;
+    uint arg1;
+    uint arg2;
+};
+
+struct instruction : public src_item {
+    rvalue sideset; // possibly null
+    rvalue delay;
+
+    instruction(const yy::location &l) : src_item(l) {}
+
+    virtual uint encode(const program &program);
+
+    virtual raw_encoding raw_encode(const program &program);
+};
+
+struct pio_assembler;
+
+// rvalue with extra encompassing location
+struct rvalue_loc {
+    rvalue value;
+    yy::location location;
+
+    rvalue_loc() = default;
+
+    rvalue_loc(const rvalue &v, const yy::location &l) : value(v), location(l) {}
+};
+
+struct program : public src_item {
+    static const int MAX_INSTRUCTIONS = 32;
+
+    pio_assembler *pioasm;
+    std::string name;
+    rvalue_loc origin;
+    rvalue_loc sideset;
+    bool sideset_opt;
+    bool sideset_pindirs;
+
+    rvalue wrap_target;
+    rvalue wrap;
+
+    std::map<std::string, std::shared_ptr<symbol>> symbols;
+    std::vector<std::shared_ptr<symbol>> ordered_symbols;
+    std::vector<std::shared_ptr<instruction>> instructions;
+    std::map<std::string, std::vector<code_block>> code_blocks;
+    std::map<std::string, std::vector<std::pair<std::string,std::string>>> lang_opts;
+
+    // post finalization
+    int delay_max;
+    int sideset_bits_including_opt; // specified side set bits + 1 if we need presence flag
+    int sideset_max;
+
+    program(pio_assembler *pioasm, const yy::location &l, std::string name) :
+            src_item(l), pioasm(pioasm), name(std::move(name)), sideset_opt(true), sideset_pindirs(false) {}
+
+    void set_origin(const yy::location &l, const rvalue &_origin) {
+        origin = rvalue_loc(_origin, l);
+    }
+
+    void set_wrap_target(const yy::location &l);
+
+    void set_wrap(const yy::location &l);
+
+    void set_sideset(const yy::location &l, rvalue _sideset, bool optional, bool pindirs) {
+        sideset = rvalue_loc(_sideset, l);
+        sideset_opt = optional;
+        sideset_pindirs = pindirs;
+    }
+
+    void add_label(std::shared_ptr<symbol> label) {
+        label->value = resolvable_int(label->location, instructions.size());
+        add_symbol(label);
+    }
+
+    void add_symbol(std::shared_ptr<symbol> symbol);
+
+    void add_instruction(std::shared_ptr<instruction> inst);
+
+    void add_code_block(const code_block &block);
+
+    void add_lang_opt(std::string lang, std::string name, std::string value);
+    void finalize();
+};
+
+struct instr_jmp : public instruction {
+    condition cond;
+    rvalue target;
+
+    instr_jmp(const yy::location &l, condition c, rvalue target) : instruction(l), cond(c), target(std::move(target)) { }
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+struct instr_wait : public instruction {
+    rvalue polarity;
+    std::shared_ptr<wait_source> source;
+
+    instr_wait(const yy::location &l, rvalue polarity, std::shared_ptr<wait_source> source) : instruction(l), polarity(
+            std::move(polarity)), source(std::move(source)) {}
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+struct instr_in : public instruction {
+    enum in_out_set src;
+    rvalue value;
+
+    instr_in(const yy::location &l, const enum in_out_set &src, rvalue value) : instruction(l), src(src),
+                                                                                value(std::move(value)) {}
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+struct instr_out : public instruction {
+    enum in_out_set dest;
+    rvalue value;
+
+    instr_out(const yy::location &l, const enum in_out_set &dest, rvalue value) : instruction(l), dest(dest),
+                                                                                  value(std::move(value)) {}
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+struct instr_set : public instruction {
+    enum in_out_set dest;
+    rvalue value;
+
+    instr_set(const yy::location &l, const enum in_out_set &dest, rvalue value) : instruction(l), dest(dest),
+                                                                                  value(std::move(value)) {}
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+
+struct instr_push : public instruction {
+    bool if_full, blocking;
+
+    instr_push(const yy::location &l, bool if_full, bool blocking) : instruction(l), if_full(if_full),
+                                                                     blocking(blocking) {}
+
+    raw_encoding raw_encode(const program &program) override {
+        uint arg1 = (blocking ? 1u : 0u) | (if_full ? 0x2u : 0);
+        return {inst_type::push_pull, arg1, 0};
+    }
+};
+
+struct instr_pull : public instruction {
+    bool if_empty, blocking;
+
+    instr_pull(const yy::location &l, bool if_empty, bool blocking) : instruction(l), if_empty(if_empty),
+                                                                      blocking(blocking) {}
+
+    raw_encoding raw_encode(const program &program) override {
+        uint arg1 = (blocking ? 1u : 0u) | (if_empty ? 0x2u : 0) | 0x4u;
+        return {inst_type::push_pull, arg1, 0};
+    }
+};
+
+struct instr_mov : public instruction {
+    enum mov dest, src;
+    mov_op op;
+
+    instr_mov(const yy::location &l, const enum mov &dest, const enum mov &src, const mov_op& op = mov_op::none) :
+            instruction(l), dest(dest), src(src), op(op) {}
+
+    raw_encoding raw_encode(const program &program) override {
+        return {inst_type::mov, (uint) dest, (uint)src | ((uint)op << 3u)};
+    }
+};
+
+struct instr_irq : public instruction {
+    enum irq modifiers;
+    rvalue num;
+    bool relative;
+
+    instr_irq(const yy::location &l, const enum irq &modifiers, rvalue num, bool relative = false) :
+            instruction(l), modifiers(modifiers), num(std::move(num)), relative(relative) {}
+
+    raw_encoding raw_encode(const program &program) override;
+};
+
+
+struct instr_nop : public instr_mov {
+    instr_nop(const yy::location &l) : instr_mov(l, mov::y, mov::y) {}
+};
+
+struct instr_word : public instruction {
+    rvalue encoding;
+
+    instr_word(const yy::location &l, rvalue encoding) : instruction(l), encoding(std::move(encoding)) {}
+
+    uint encode(const program &program) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/tools/pioasm/python_output.cpp b/tools/pioasm/python_output.cpp
new file mode 100644
index 0000000..38f7ffe
--- /dev/null
+++ b/tools/pioasm/python_output.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <array>
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include "output_format.h"
+#include "pio_disassembler.h"
+
+struct python_output : public output_format {
+    struct factory {
+        factory() {
+            output_format::add(new python_output());
+        }
+    };
+
+    python_output() : output_format("python") {}
+
+    std::string get_description() override {
+        return "Python file suitable for use with MicroPython";
+    }
+
+    void output_symbols(FILE *out, std::string prefix, const std::vector<compiled_source::symbol> &symbols) {
+        int count = 0;
+        for (const auto &s : symbols) {
+            if (!s.is_label) {
+                fprintf(out, "%s%s = %d\n", prefix.c_str(), s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+            count = 0;
+        }
+        for (const auto &s : symbols) {
+            if (s.is_label) {
+                fprintf(out, "%soffset_%s = %d\n", prefix.c_str(), s.name.c_str(), s.value);
+                count++;
+            }
+        }
+        if (count) {
+            fprintf(out, "\n");
+        }
+    }
+
+    void header(FILE *out, std::string msg) {
+        std::string dashes = std::string(msg.length(), '-');
+        fprintf(out, "# %s #\n", dashes.c_str());
+        fprintf(out, "# %s #\n", msg.c_str());
+        fprintf(out, "# %s #\n", dashes.c_str());
+        fprintf(out, "\n");
+    }
+
+    int output(std::string destination, std::vector<std::string> output_options,
+               const compiled_source &source) override {
+        FILE *out = open_single_output(destination);
+        if (!out) return 1;
+
+        header(out, "This file is autogenerated by pioasm; do not edit!");
+
+        fprintf(out, "import rp2\n");
+        fprintf(out, "from machine import Pin");
+        fprintf(out, "\n");
+
+        output_symbols(out, "", source.global_symbols);
+
+        for (const auto &program : source.programs) {
+            header(out, program.name);
+
+            std::string prefix = program.name + "_";
+
+            output_symbols(out, prefix, program.symbols);
+
+            int param_count = 0;
+            auto write_opt = [&] (std::string name, std::string value) {
+                if (param_count++) {
+                    fprintf(out, ", ");
+                }
+                fprintf(out, "%s=%s", name.c_str(), value.c_str());
+            };
+            fprintf(out, "@rp2.asm_pio(");
+            for(const auto &p : program.lang_opts) {
+                if (p.first.size() >= name.size() && p.first.compare(0, name.size(), name) == 0) {
+                    for (const auto &p2 : p.second) {
+                        write_opt(p2.first, p2.second);
+                    }
+                }
+            }
+            fprintf(out, ")\n");
+            fprintf(out, "def %s():\n", program.name.c_str());
+
+            std::map<uint, std::string> jmp_labels;
+            // for now just use numeric labels
+            for (int i = 0; i < (int)program.instructions.size(); i++) {
+                const auto &inst = program.instructions[i];
+                if (!(inst >> 13u)) {
+                    // a jump
+                    uint target = inst &0x1fu;
+                    jmp_labels.insert(std::pair<uint,std::string>(target, std::to_string(target)));
+                }
+            }
+
+            for (uint i = 0; i < (int)program.instructions.size(); i++) {
+                const auto &inst = program.instructions[i];
+                if (i == program.wrap_target) {
+                    fprintf(out, "    wrap_target()\n");
+                }
+                auto it = jmp_labels.find(i);
+                if (it != jmp_labels.end()) {
+                    fprintf(out, "    label(\"%s\")\n", it->second.c_str());
+                }
+                fprintf(out, "    %s # %d\n", disassemble(jmp_labels, inst, program.sideset_bits_including_opt.get(), program.sideset_opt).c_str(), i);
+                if (i == program.wrap) {
+                    fprintf(out, "    wrap()\n");
+                }
+            }
+            fprintf(out, "\n");
+
+            /*
+            fprintf(out, "static inline pio_sm_config %sprogram_default_config(uint offset) {\n", prefix.c_str());
+            fprintf(out, "    pio_sm_config c = pio_sm_default_config();\n");
+            fprintf(out, "    sm_config_wrap(&c, offset + %swrap_target, offset + %swrap);\n", prefix.c_str(),
+                    prefix.c_str());
+            if (program.sideset_bits_including_opt.is_specified()) {
+                fprintf(out, "    sm_config_sideset(&c, %d, %s, %s);\n", program.sideset_bits_including_opt.get(),
+                        program.sideset_opt ? "true" : "false",
+                        program.sideset_pindirs ? "true" : "false");
+            }
+            fprintf(out, "    return c;\n");
+            fprintf(out, "}\n");
+*/
+            // todo maybe have some code blocks inside or outside here?
+            for(const auto& o : program.code_blocks) {
+                fprintf(out, "\n");
+                if (o.first == name) {
+                    for(const auto &contents : o.second) {
+                        fprintf(out, "%s", contents.c_str());
+                        fprintf(out, "\n");
+                    }
+                }
+            }
+
+            fprintf(out, "\n");
+        }
+        if (out != stdout) { fclose(out); }
+        return 0;
+    }
+
+    static std::string disassemble(const std::map<uint, std::string>& jmp_labels, uint16_t inst, uint sideset_bits_including_opt, bool sideset_opt) {
+        std::stringstream ss;
+        uint major = inst >> 13u;
+        uint arg1 = ((uint) inst >> 5u) & 0x7u;
+        uint arg2 = inst & 0x1fu;
+        std::string op_string;
+        auto op = [&](const std::string &s) {
+            op_string = s;
+        };
+        auto op_guts = [&](const std::string &s) {
+            ss << std::left << std::setw(24) << (op_string + "(" + s + ")");
+        };
+
+        bool invalid = false;
+        switch (major) {
+            case 0b000: {
+                static std::array<std::string, 8> conditions{"", "not_x", "x_dec", "not_y", "y_dec", "x_not_y", "pin",
+                                                             "not_osre"};
+                op("jmp");
+                auto it = jmp_labels.find(arg2);
+                std::string label = "?";
+                if (it != jmp_labels.end()) {
+                    label = it->second;
+                }
+                if (arg1)
+                    op_guts(conditions[arg1] + ", \"" + label +"\"");
+                else
+                    op_guts("\"" + label + "\"");
+                break;
+            }
+            case 0b001: {
+                uint source = arg1 & 3u;
+                std::string guts;
+                switch (source) {
+                    case 0b00:
+                        guts = "gpio, " + std::to_string(arg2);
+                        break;
+                    case 0b01:
+                        guts = "pin, " + std::to_string(arg2);
+                        break;
+                    case 0b10:
+                        if (arg2 & 0x8u) {
+                            invalid = true;
+                        } else {
+                            guts = "irq, ";
+                            auto irq = std::to_string(arg2 & 7u);
+                            if (arg2 & 0x10u) {
+                                guts += "rel(" + irq + ")";
+                            } else {
+                                guts += irq;
+                            }
+                        }
+                        break;
+                }
+                if (!invalid) {
+                    guts = ((arg1 & 4u) ? "1, " : "0, ") + guts;
+                    op("wait");
+                    op_guts(guts);
+                }
+                break;
+            }
+            case 0b010: {
+                static std::array<std::string, 8> sources { "pins", "x", "y", "null", "", "status", "isr", "osr"};
+                std::string source = sources[arg1];
+                if (source.empty()) {
+                    invalid = true;
+                } else {
+                    op("in_");
+                    op_guts(source + ", " + std::to_string(arg2 ? arg2 : 32));
+                }
+                break;
+            }
+            case 0b011: {
+                static std::array<std::string, 8> dests { "pins", "x", "y", "null", "pindirs", "pc", "isr", "exec"};
+                op("out");
+                op_guts(dests[arg1] + ", " + std::to_string(arg2 ? arg2 : 32));
+                break;
+            }
+            case 0b100: {
+                if (arg2) {
+                    invalid = true;
+                } else {
+                    std::string guts = "";
+                    if (arg1 & 4u) {
+                        op("pull");
+                        if (arg1 & 2u) guts = "ifempty, ";
+                    } else {
+                        op("push");
+                        if (arg1 & 2u) guts = "iffull, ";
+                    }
+                    guts += ((arg1 & 0x1u) ? "block" : "noblock");
+                    op_guts(guts);
+                }
+                break;
+            }
+            case 0b101: {
+                static std::array<std::string, 8> dests { "pins", "x", "y", "", "exec", "pc", "isr", "osr"};
+                static std::array<std::string, 8> sources { "pins", "x", "y", "null", "", "status", "isr", "osr"};
+                std::string dest = dests[arg1];
+                std::string source = sources[arg2 & 7u];
+                uint operation = arg2 >> 3u;
+                if (source.empty() || dest.empty() || operation == 3) {
+                    invalid = true;
+                }
+                if (dest == source && (arg1 == 1 || arg2 == 2)) {
+                    op("nop");
+                    op_guts("");
+                } else {
+                    op("mov");
+                    std::string guts = dest + ", ";
+                    if (operation == 1) {
+                        guts += "invert(";
+                    } else if (operation == 2) {
+                        guts += "reverse(";
+                    }
+                    guts += source;
+                    if (operation == 1 || operation == 2) {
+                        guts += ")";
+                    }
+                    op_guts(guts);
+                }
+                break;
+            }
+            case 0b110: {
+                if ((arg1 & 0x4u) || (arg2 & 0x8u)) {
+                    invalid = true;
+                } else {
+                    op("irq");
+                    std::string guts;
+                    if (arg1 & 0x2u) {
+                        guts += "clear, ";
+                    } else if (arg1 & 0x1u) {
+                        guts += "wait, ";
+                    } else {
+                        guts += "nowait, ";
+                    }
+                    auto irq = std::to_string(arg2 & 7u);
+                    if (arg2 & 0x10u) {
+                        guts += "rel(" + irq + ")";
+                    } else {
+                        guts += irq;
+                    }
+                    op_guts(guts);
+                }
+                break;
+            }
+            case 0b111: {
+                static std::array<std::string, 8> dests{"pins", "x", "y", "", "pindirs", "", "", ""};
+                std::string dest = dests[arg1];
+                if (dest.empty()) {
+                    invalid = true;
+                } else {
+                    op("set");
+                    op_guts(dests[arg1] + ", " + std::to_string(arg2));
+                }
+                break;
+            }
+        }
+        if (invalid) {
+            op("word");
+            ss << std::hex;
+            op_guts(std::to_string(inst));
+        }
+        uint delay = ((uint) inst >> 8u) & 0x1f;
+        ss << std::left << std::setw(9);
+        if (sideset_bits_including_opt && (!sideset_opt || (delay & 0x10u))) {
+            ss << (".side("+ std::to_string((delay & (sideset_opt ? 0xfu : 0x1fu)) >> (5u - sideset_bits_including_opt))+")");
+        } else {
+            ss << "";
+        }
+        delay &= ((1u << (5 - sideset_bits_including_opt)) - 1u);
+        ss << std::left << std::setw(4) << (delay ? ("[" + std::to_string(delay) + "]") : "");
+        return ss.str();
+    }
+};
+
+static python_output::factory creator;