Full Code of fredoverflow/skorbut-release for AI

master b9eff51240d4 cached
63 files
292.6 KB
72.9k tokens
1 requests
Download .txt
Showing preview only (313K chars total). Download the full file or copy to clipboard to get everything.
Repository: fredoverflow/skorbut-release
Branch: master
Commit: b9eff51240d4
Files: 63
Total size: 292.6 KB

Directory structure:
gitextract_y9tqg1bi/

├── .gitignore
├── README.md
├── pom.xml
├── sloc
└── src/
    ├── main/
    │   └── kotlin/
    │       ├── Main.kt
    │       ├── common/
    │       │   ├── Counter.kt
    │       │   ├── Diagnostic.kt
    │       │   └── Maps.kt
    │       ├── interpreter/
    │       │   ├── BasicBlock.kt
    │       │   ├── BuildControlFlowGraph.kt
    │       │   ├── Console.kt
    │       │   ├── FlatStatements.kt
    │       │   ├── Interpreter.kt
    │       │   ├── Memory.kt
    │       │   ├── Segment.kt
    │       │   └── Value.kt
    │       ├── semantic/
    │       │   ├── Linter.kt
    │       │   ├── LinterBase.kt
    │       │   ├── SymbolTable.kt
    │       │   ├── TypeChecker.kt
    │       │   ├── TypeSpecifiers.kt
    │       │   └── types/
    │       │       ├── Arithmetic.kt
    │       │       ├── Array.kt
    │       │       ├── Enum.kt
    │       │       ├── Function.kt
    │       │       ├── Pointer.kt
    │       │       ├── Struct.kt
    │       │       ├── Type.kt
    │       │       ├── Typedef.kt
    │       │       └── Void.kt
    │       ├── syntax/
    │       │   ├── lexer/
    │       │   │   ├── Characters.kt
    │       │   │   ├── Identifiers.kt
    │       │   │   ├── Lexer.kt
    │       │   │   ├── NextToken.kt
    │       │   │   ├── Numbers.kt
    │       │   │   ├── SkipComments.kt
    │       │   │   ├── Token.kt
    │       │   │   ├── TokenKind.kt
    │       │   │   └── TokenKindSet.kt
    │       │   ├── parser/
    │       │   │   ├── Autocompletion.kt
    │       │   │   ├── Declarations.kt
    │       │   │   ├── Expressions.kt
    │       │   │   ├── ExternalDefinitions.kt
    │       │   │   ├── LeftDenotations.kt
    │       │   │   ├── NullDenotations.kt
    │       │   │   ├── Parser.kt
    │       │   │   └── Statements.kt
    │       │   └── tree/
    │       │       ├── DeclarationSpecifier.kt
    │       │       ├── Declarator.kt
    │       │       ├── Expression.kt
    │       │       ├── External.kt
    │       │       ├── Node.kt
    │       │       └── Statement.kt
    │       ├── text/
    │       │   ├── Char.kt
    │       │   └── String.kt
    │       └── ui/
    │           ├── Flexer.kt
    │           ├── MainFrame.kt
    │           └── MemoryUI.kt
    └── test/
        ├── kotlin/
        │   ├── interpreter/
        │   │   └── InterpreterTest.kt
        │   ├── semantic/
        │   │   └── types/
        │   │       └── TypeToStringTest.kt
        │   └── syntax/
        │       ├── lexer/
        │       │   └── LexerTest.kt
        │       └── parser/
        │           └── AutocompletionTest.kt
        └── resources/
            └── junit-platform.properties

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/target/
/.idea/
/*.iml


================================================
FILE: README.md
================================================
![swap](skorbut.png)

## What is Skorbut?

Skorbut is a simple teaching environment for a subset of C with a memory visualizer.
If you ever had trouble visualizing arrays and pointers, you have come to the right place.

*Skorbut* is German for *scurvy*, an illness that is caused by a lack of Vitamin C.
Skorbut also lacks C in the sense that it implements only a restricted subset of C.

## Getting started

Please take the time to **read the following instructions carefully.**
Most problems stem from skipping or misunderstanding important steps.

### ☕ Windows & macOS

1. Visit https://adoptium.net

2. Click "Latest release" button to download Java installer

3. Wait for download to finish

4. Open the `Downloads` folder (via Windows Explorer or Finder/Spotlight, respectively) and double-click `OpenJDK...` to start Java installer

5. Click Next, Next, Install, Finish

6. Click [skorbut.jar](https://raw.githubusercontent.com/fredoverflow/skorbut/release/skorbut.jar) to download Skorbut<br>
**If Skorbut fails to download**, continue with ⚠️ Troubleshooting *Windows*, or ⚠️ Troubleshooting *macOS*

7. Open the `Downloads` folder and double-click `skorbut.jar` to start Skorbut<br>
**If Skorbut fails to start**, continue with ⚠️ Troubleshooting *Windows*, or ⚠️ Troubleshooting *macOS*

### ⚠️ Troubleshooting *Windows*

Steps 1 through 5 (install Java) worked, but steps 6 (download Skorbut) or 7 (start Skorbut) failed? Then read on.

- Move your mouse over the script below
- A button appears in the top right corner of the script
- Click that button to copy the script
```cmd
cd Downloads
if exist skorbut.jar.zip erase skorbut.jar.zip
curl -o skorbut.jar https://raw.githubusercontent.com/fredoverflow/skorbut/release/skorbut.jar
echo java -version > skorbut.cmd
echo java -jar skorbut.jar >> skorbut.cmd
skorbut.cmd

```
- Press the Windows key (the key on the bottom left with the Windows logo ⊞ on it)
- Write `cmd` and confirm with Enter
- A terminal appears
- Right-click anywhere inside that terminal to paste and execute the script

From now on, simply double-click `skorbut.cmd` in the `Downloads` folder to start Skorbut.<br>
Feel free to move `skorbut.jar` and `skorbut.cmd` to the Desktop or any other folder you prefer.

### ⚠️ Troubleshooting *macOS*

Steps 1 through 5 (install Java) worked, but steps 6 (download Skorbut) or 7 (start Skorbut) failed? Then read on.

- Move your mouse over the script below
- A button appears in the top right corner of the script
- Click that button to copy the script
```sh
cd Downloads
curl -o skorbut.jar https://raw.githubusercontent.com/fredoverflow/skorbut/release/skorbut.jar
chmod +x skorbut.jar
echo java -version > skorbut.sh
echo java -jar skorbut.jar >> skorbut.sh
chmod +x skorbut.sh
./skorbut.sh

```
- Press `Command⌘ Space` (or click the magnifying glass 🔍 in the top right corner of the screen) to open Spotlight
- Write `terminal` and confirm with Enter
- A terminal appears
- Press `Command⌘ V` to paste and execute the script

From now on, simply double-click `skorbut.sh` in the `Downloads` folder to start Skorbut.<br>
Feel free to move `skorbut.jar` and `skorbut.sh` to the Desktop or any other folder you prefer.

### 🐧 Ubuntu, Linux Mint, Debian...

```sh
sudo apt install default-jdk
cd Downloads
curl -o skorbut.jar https://raw.githubusercontent.com/fredoverflow/skorbut/release/skorbut.jar
chmod +x skorbut.jar
echo java -version > skorbut.sh
echo java -jar -Dsun.java2d.opengl=True skorbut.jar >> skorbut.sh
chmod +x skorbut.sh
./skorbut.sh

```

From now on, simply double-click `skorbut.sh` in the `Downloads` folder to start Skorbut.<br>
Feel free to move `skorbut.jar` and `skorbut.sh` to the Desktop or any other folder you prefer.

### I would rather compile Skorbut from source!

```
git clone https://github.com/fredoverflow/freditor
cd freditor
mvn install
cd ..
git clone https://github.com/fredoverflow/skorbut
cd skorbut
mvn package
```

The executable `skorbut.jar` will be located inside the `target` folder.

## How do I save my code?

The code is automatically saved to a new file each time you click the start button.
The save folder is named `skorbut`, and it is located in your home directory.
The full path is displayed in the title bar.

## Does Skorbut support auto-indentation?

Yes, just hit Enter or Tab.

## What about auto-completion?

Ctrl+Space after an identifier auto-completes to the longest common prefix of all identifiers in scope.
For example, if `foo`, `bar` and `baz` are in scope, then `f` will be auto-completed to `foo`,
but `b` will only be auto-completed to `ba`, because both `bar` and `baz` start with `ba`.

Skorbut has scope-aware auto-completion for all identifiers *except* `struct` members;
those are auto-completed globally, i.e. after `.` or `->`, *all* `struct` members are considered for auto-completion.
(That's because auto-completion has no type information available yet. Changing this would be a Herculean task.)

## What features of C are currently missing?

Non-exhaustive list off the top of my head:

| Feature             | Priority |
| ------------------- | -------- |
| preprocessor        | very low |
| variadic functions  | very low |
| compound assignment | low      |
| null pointer        | medium   |
| union               | very low |
| return struct       | low      |

## Wait, no preprocessor? How do I `#include <stdio.h>`?

You don't need to include anything, the following standard library functions are already available:

- printf
- scanf
- puts
- putchar
- getchar
- malloc
- free
- realloc
- qsort
- bsearch
- strlen
- strcmp
- pow
- time

## What about my own header files?

Skorbut does not support multiple translation units, so there would be no point in supporting header files.

## How do I define constants without `#define`?

For integral constants, you can use anonymous enumerations like `enum { N = 10 };`

## Keyboard shortcuts

| Windows      | Effect                | Macintosh        |
| -----------: | :-------------------: | ---------------- |
| F1           | show type             | F1               |
| F3           | declaration<br>usages | F3               |
| F5           | step into             | F5               |
| F6           | step over             | F6               |
| F7           | step return           | F7               |
| Tab<br>Enter | auto-indent           | Tab<br>Enter     |
| Ctrl Space   | auto-complete         | Control (Shift) Space |
| Ctrl Alt R   | rename symbol         | Command Option R |
| Ctrl D       | delete line           | Command D        |
| Ctrl C       | copy                  | Command C        |
| Ctrl X       | cut                   | Command X        |
| Ctrl V       | paste                 | Command V        |
| Ctrl Z       | undo                  | Command Z        |
| Ctrl Y       | redo                  | Command Y        |


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fredoverflow</groupId>
    <artifactId>skorbut</artifactId>
    <version>0.1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <kotlin.code.style>official</kotlin.code.style>
        <kotlin.version>2.2.21</kotlin.version>
        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <main.class>MainKt</main.class>
        <java.runtime>${java.home}/lib/rt.jar</java.runtime>
    </properties>

    <profiles>
        <profile>
            <id>java-modules</id>
            <activation>
                <jdk>[9,)</jdk>
            </activation>
            <properties>
                <java.runtime>${java.home}/jmods(!**.jar;!module-info.class)</java.runtime>
            </properties>
        </profile>
    </profiles>

    <dependencies>
        <dependency>
            <groupId>fredoverflow</groupId>
            <artifactId>freditor</artifactId>
            <version>0.1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.14.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src/main/kotlin</sourceDirectory>
        <testSourceDirectory>src/test/kotlin</testSourceDirectory>

        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>

                    <execution>
                        <id>test-compile</id>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.4</version>
            </plugin>

            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>${main.class}</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>2.6.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includeDependencyInjar>true</includeDependencyInjar>
                    <outFilter>META-INF/MANIFEST.MF,!META-INF/**,!**.kotlin_*</outFilter>
                    <outjar>${project.artifactId}.jar</outjar>
                    <options>
                        <!-- for some reason, the usual approach via configuration/libs/lib does not work -->
                        <option>-libraryjars ${java.runtime}</option>
                        <!-- preserve entry point, otherwise output jar would be empty -->
                        <option>-keep public class ${main.class} { public static void main(java.lang.String[]); }</option>
                        <!-- remove compiler-generated null checks for unneeded Java->Kotlin interoperability -->
                        <option>-assumenosideeffects class kotlin.jvm.internal.Intrinsics { static void checkParameterIsNotNull(java.lang.Object, java.lang.String); }</option>
                        <!-- hide annoying but harmless reflection warnings -->
                        <option>-dontnote kotlin.**</option>
                    </options>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


================================================
FILE: sloc
================================================
# sloc: count significant lines of code
# INsignificant lines contain only spaces and/or braces

# $1 directory
# $2 extension
function countInDirectory {
	lines=$(find "src/$1" -name "*.$2" -exec grep -vP "^[{ }]*\r?$" {} + | wc -l)
	if [ $lines -gt 0 ]
	then
		printf "$1: $lines\n"
	fi
}

# $1 language
# $2 extension
function countForLanguage {
	printf "$1\n"
	printf "==========\n"
	countInDirectory main $2
	countInDirectory test $2
	printf "\n"
}

countForLanguage "Kotlin" "kt"


================================================
FILE: src/main/kotlin/Main.kt
================================================
import freditor.SwingConfig
import ui.MainFrame
import java.awt.EventQueue

fun main() {
    SwingConfig.metalWithDefaultFont(SwingConfig.SANS_SERIF_PLAIN_16)
    EventQueue.invokeLater(::MainFrame)
}


================================================
FILE: src/main/kotlin/common/Counter.kt
================================================
package common

class Counter {
    private val counter = HashMap<Any, Int>()

    fun count(x: Any): Int {
        val soFar = counter.getOrElse(x) { 0 }
        counter[x] = soFar + 1
        return soFar
    }
}


================================================
FILE: src/main/kotlin/common/Diagnostic.kt
================================================
package common

data class Diagnostic(
    val position: Int, override val message: String,
    val secondPosition: Int = -1,
    val columnDelta: Int = 0
) : Exception(message) {

    override fun toString(): String {
        return if (secondPosition < 0) {
            message
        } else {
            "$message \uD83D\uDDB0 toggle position"
        }
    }
}


================================================
FILE: src/main/kotlin/common/Maps.kt
================================================
package common

fun <K, V, M : MutableMap<K, V>> M.puts(keys: Array<out K>, value: V): M {
    for (key in keys) {
        put(key, value)
    }
    return this
}

fun <K, V, M : MutableMap<K, V>> M.puts(k1: K, k2: K, value: V): M {
    put(k1, value)
    put(k2, value)
    return this
}

fun <K, V, M : MutableMap<K, V>> M.puts(k1: K, k2: K, k3: K, k4: K, value: V): M {
    put(k1, value)
    put(k2, value)
    put(k3, value)
    put(k4, value)
    return this
}

fun <K, V, M : MutableMap<K, V>> M.puts(k1: K, k2: K, k3: K, k4: K, k5: K, k6: K, value: V): M {
    put(k1, value)
    put(k2, value)
    put(k3, value)
    put(k4, value)
    put(k5, value)
    put(k6, value)
    return this
}


================================================
FILE: src/main/kotlin/interpreter/BasicBlock.kt
================================================
package interpreter

class BasicBlock {
    private val statements = ArrayList<FlatStatement>()
    fun getStatements(): List<FlatStatement> = statements

    fun isOpen(): Boolean {
        return !isClosed()
    }

    fun isClosed(): Boolean {
        return statements.lastOrNull() is TransferringControl
    }

    fun isEmpty(): Boolean {
        return statements.isEmpty()
    }

    fun add(statement: FlatStatement) {
        assert(isOpen())
        statements.add(statement)
    }

    fun replaceSwitchPlaceholderWithRealSwitch(replacement: HashSwitch) {
        assert(statements.last() === SwitchPlaceholder)
        statements[statements.lastIndex] = replacement
    }

    var isReachable = false
        private set

    fun exploreReachability(resolve: (String) -> BasicBlock) {
        if (!isReachable) {
            isReachable = true
            statements.lastOrNull()?.forEachSuccessor { label ->
                resolve(label).exploreReachability(resolve)
            }
        }
    }
}


================================================
FILE: src/main/kotlin/interpreter/BuildControlFlowGraph.kt
================================================
package interpreter

import semantic.types.ArithmeticType
import syntax.lexer.missingIdentifier
import syntax.tree.*

val unusedEmptyHashMap = HashMap<ArithmeticValue, String>()

class State(
    val continueTarget: String, val breakTarget: String,
    val switchControlType: ArithmeticType?,
    val cases: HashMap<ArithmeticValue, String>, var default: String?
) {
    constructor() : this("", "", null, unusedEmptyHashMap, null)

    fun openLoop(continueTarget: String, breakTarget: String): State {
        return State(continueTarget, breakTarget, switchControlType, cases, default)
    }

    fun openSwitch(controlType: ArithmeticType, breakTarget: String): State {
        return State(continueTarget, breakTarget, controlType, HashMap(), null)
    }
}

class BuildControlFlowGraph(function: FunctionDefinition) {
    private val controlFlowGraph = LinkedHashMap<String, BasicBlock>()

    private var lastGeneratedLabel = -1
    private var currentLabelStr = ""
    private var currentBasicBlock = BasicBlock()

    private fun generateLabel(): String {
        return "${++lastGeneratedLabel}"
    }

    private fun insertLabel(label: String) {
        if (!currentBasicBlock.isEmpty()) {
            jumpIfOpen(label)
            currentBasicBlock = BasicBlock()
        }
        currentLabelStr = label
        controlFlowGraph[currentLabelStr] = currentBasicBlock
    }

    private fun add(statement: FlatStatement) {
        if (currentBasicBlock.isClosed()) {
            currentBasicBlock = BasicBlock()
            currentLabelStr = generateLabel()
            controlFlowGraph[currentLabelStr] = currentBasicBlock
        }
        currentBasicBlock.add(statement)
    }

    private fun jumpIfOpen(target: String) {
        if (currentBasicBlock.isOpen()) {
            add(Jump(missingIdentifier, target))
        }
    }

    init {
        currentLabelStr = generateLabel()
        insertLabel(currentLabelStr)

        function.body.flatten(State())

        val entry = controlFlowGraph.values.first()
        entry.exploreReachability { controlFlowGraph[it]!! }
        function.controlFlowGraph = controlFlowGraph
    }

    private fun List<Statement>.flatten(state: State) {
        for (statement in this) {
            statement.flatten(state)
        }
    }

    private fun Statement.flatten(state: State) {
        when (this) {
            is Block -> {
                statements.flatten(state)
            }

            is Declaration -> {
                add(FlatDeclaration(specifiers, namedDeclarators))
            }

            is ExpressionStatement -> {
                add(FlatExpressionStatement(expression))
            }

            is LabeledStatement -> {
                insertLabel(label.text)
                statement.flatten(state)
            }

            is Goto -> {
                add(Jump(goto, label.text))
            }

            is IfThenElse -> {
                if (e1se == null) {
                    val execute = generateLabel()
                    val done = generateLabel()

                    add(JumpIf(condition, execute, done))

                    insertLabel(execute)
                    th3n.flatten(state)

                    insertLabel(done)
                } else {
                    val executeThen = generateLabel()
                    val executeElse = generateLabel()
                    val done = generateLabel()

                    add(JumpIf(condition, executeThen, executeElse))

                    insertLabel(executeThen)
                    th3n.flatten(state)
                    jumpIfOpen(done)

                    insertLabel(executeElse)
                    e1se.flatten(state)

                    insertLabel(done)
                }
            }

            is Switch -> {
                val done = generateLabel()

                @Suppress("NAME_SHADOWING")
                val state = state.openSwitch(control.type.unqualified() as ArithmeticType, breakTarget = done)

                add(SwitchPlaceholder)
                val basicBlock = currentBasicBlock
                body.flatten(state)
                basicBlock.replaceSwitchPlaceholderWithRealSwitch(
                    HashSwitch(control, state.cases, state.default ?: done)
                )

                insertLabel(done)
            }

            is Case -> {
                if (state.switchControlType == null) {
                    case.error("case label must be nested inside a switch")
                }
                val caseLabel = generateLabel()
                insertLabel(caseLabel)
                val previous = state.cases.put(
                    state.switchControlType.integralPromotions().cast(choice.value as ArithmeticValue),
                    caseLabel
                )
                if (previous != null) {
                    case.error("duplicate case label")
                }
                body.flatten(state)
            }

            is Default -> {
                if (state.switchControlType == null) {
                    default.error("default label must be nested inside a switch")
                }
                if (state.default != null) {
                    default.error("duplicate default label")
                }
                val defaultLabel = generateLabel()
                insertLabel(defaultLabel)
                state.default = defaultLabel
                body.flatten(state)
            }

            is Do -> {
                val bodyStart = generateLabel()
                val checkCondition = generateLabel()
                val done = generateLabel()

                @Suppress("NAME_SHADOWING")
                val state = state.openLoop(continueTarget = checkCondition, breakTarget = done)

                insertLabel(bodyStart)
                body.flatten(state)

                insertLabel(checkCondition)
                add(JumpIf(condition, bodyStart, done))

                insertLabel(done)
            }

            is While -> {
                val checkCondition = generateLabel()
                val bodyStart = generateLabel()
                val done = generateLabel()

                @Suppress("NAME_SHADOWING")
                val state = state.openLoop(continueTarget = checkCondition, breakTarget = done)

                insertLabel(checkCondition)
                add(JumpIf(condition, bodyStart, done))

                insertLabel(bodyStart)
                body.flatten(state)
                add(ImplicitContinue(whi1e, checkCondition))

                insertLabel(done)
            }

            is For -> {
                when (init) {
                    is ExpressionStatement -> {
                        add(FlatExpressionStatement(init.expression))
                    }

                    is Declaration -> {
                        add(FlatDeclaration(init.specifiers, init.namedDeclarators))
                    }
                }

                val checkCondition = generateLabel()
                val loopStart = generateLabel()
                val updateCounter = generateLabel()
                val done = generateLabel()

                @Suppress("NAME_SHADOWING")
                val state = state.openLoop(continueTarget = updateCounter, breakTarget = done)

                if (condition != null) {
                    insertLabel(checkCondition)
                    add(JumpIf(condition, loopStart, done))

                    insertLabel(loopStart)
                    body.flatten(state)

                    insertLabel(updateCounter)
                    if (update != null) {
                        add(FlatExpressionStatement(update))
                    }
                    add(ImplicitContinue(f0r, checkCondition))

                    insertLabel(done)
                } else {
                    insertLabel(loopStart)
                    body.flatten(state)

                    insertLabel(updateCounter)
                    if (update != null) {
                        add(FlatExpressionStatement(update))
                    }
                    add(ImplicitContinue(f0r, loopStart))

                    insertLabel(done)
                }
            }

            is Continue -> {
                if (state.continueTarget.isEmpty()) {
                    continu3.error("continue must be nested inside a loop")
                }
                add(Jump(continu3, state.continueTarget))
            }

            is Break -> {
                if (state.breakTarget.isEmpty()) {
                    br3ak.error("break must be nested inside a loop or switch")
                }
                add(Jump(br3ak, state.breakTarget))
            }

            is Return -> {
                add(FlatReturn(r3turn, result))
            }

            is Assert -> {
                add(FlatAssert(condition))
            }

            else -> {
                error("no flatten for $this")
            }
        }
    }
}


================================================
FILE: src/main/kotlin/interpreter/Console.kt
================================================
package interpreter

import semantic.types.DoubleType
import semantic.types.FloatType
import semantic.types.SignedCharType
import semantic.types.SignedIntType
import syntax.lexer.Token
import text.skipDigits

import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.atomic.AtomicBoolean

class Console {
    private val output = StringBuilder()

    var isDirty: Boolean = false
        private set

    private var input = StringBuilder()

    private val queue = LinkedBlockingDeque<Char>()
    private val blocked = AtomicBoolean(false)

    fun isBlocked(): Boolean = blocked.get()

    var update: Function0<Unit>? = null

    fun puts(str: PointerValue) {
        print(stringStartingAt(str))
        putchar('\n')
    }

    fun putchar(x: Char) {
        output.append(x)
        isDirty = true
    }

    fun print(x: CharSequence) {
        output.append(x)
        isDirty = true
    }

    fun printf(format: Token, arguments: List<Value>): Int {
        val sb = StringBuilder()
        val args = arguments.iterator()
        val fmt = format.text
        var i = 0
        var k = fmt.indexOf('%')
        while (k != -1) {
            sb.append(fmt, i, k)
            if (fmt[++k] == '%') {
                sb.append('%')
            } else {
                i = k
                k = fmt.skipDigits(i) // width
                if (fmt[k] == '.') {
                    k = fmt.skipDigits(k + 1) // precision
                }
                val specifier = fmt.substring(i - 1, k + 1)
                sb.append(formatValue(args.next(), specifier))
            }
            i = k + 1
            k = fmt.indexOf('%', i)
        }
        sb.append(fmt, i, fmt.length)
        print(sb)
        return sb.length
    }

    private fun formatValue(value: Value, specifier: String): String {
        return when (specifier.last()) {
            'c' -> specifier.format((value as ArithmeticValue).value.toLong().toInt().and(0xff))

            'i' -> specifier.replace('i', 'd').format((value as ArithmeticValue).value.toLong().toInt())

            'u' -> specifier.replace('u', 'd').format((value as ArithmeticValue).value.toLong().and(0xffffffff))

            'd', 'o', 'x', 'X' -> specifier.format((value as ArithmeticValue).value.toLong().toInt())

            'e', 'E', 'f', 'g', 'G' -> specifier.format(java.util.Locale.ENGLISH, (value as ArithmeticValue).value)

            's' -> specifier.format(stringStartingAt(value as PointerValue))

            'p' -> specifier.replace('p', 's').format(value.show())

            else -> error("illegal conversion specifier %${specifier.last()}")
        }
    }

    private fun stringStartingAt(start: PointerValue): CharSequence {
        val sb = StringBuilder()
        var ptr = start
        var x = (ptr.referenced.evaluate() as ArithmeticValue).value
        while (x != 0.0) {
            sb.append(x.toInt().and(0xff).toChar())
            ptr += 1
            if (ptr.referenced.isSentinel()) error("missing NUL terminator")
            x = (ptr.referenced.evaluate() as ArithmeticValue).value
        }
        return sb
    }

    fun scanf(format: Token, arguments: List<Value>, after: Function0<Unit>?): Int {
        val fmt = format.text
        var i = 0
        var a = 0
        while (i < fmt.length) {
            when (fmt[i++]) {
                '\t', '\n', ' ' -> skipWhitespace()

                '%' -> {
                    val percent = i - 1
                    if (i == fmt.length) format.stringErrorAt(percent, "incomplete conversion specifier")
                    if (a == arguments.size) format.stringErrorAt(percent, "missing argument after format string")
                    val arg = arguments[a++] as PointerValue
                    val referenced = arg.referenced
                    val type = referenced.type
                    when (fmt[i++]) {
                        'd' -> {
                            if (type !== SignedIntType) format.stringErrorAt(percent, "%d expects int*, not ${type.pointer()}")
                            skipWhitespace()
                            val x = scanInt() ?: return a - 1
                            referenced.assign(Value.signedInt(x))
                        }

                        'f' -> {
                            if (type !== FloatType) format.stringErrorAt(percent, "%f expects float*, not ${type.pointer()}")
                            skipWhitespace()
                            val x = scanDouble() ?: return a - 1
                            referenced.assign(Value.float(x.toFloat()))
                        }

                        'l' -> {
                            if (i == fmt.length || fmt[i] != 'f') format.stringErrorAt(i, "missing f after %l")
                            ++i
                            if (type !== DoubleType) format.stringErrorAt(percent, "%lf expects double*, not ${type.pointer()}")
                            skipWhitespace()
                            val x = scanDouble() ?: return a - 1
                            referenced.assign(Value.double(x))
                        }

                        'c' -> {
                            if (type !== SignedCharType) format.stringErrorAt(percent, "%c expects char*, not ${type.pointer()}")
                            referenced.assign(Value.signedChar(getchar()))
                        }

                        's' -> {
                            if (type !== SignedCharType) format.stringErrorAt(percent, "%s expects char*, not ${type.pointer()}")
                            val maxLen = referenced.bound - referenced.index - 1
                            format.stringErrorAt(percent, "%s is unsafe\nuse %${maxLen}s instead")
                        }

                        '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
                            var len = fmt[i - 1] - '0'
                            while (i < fmt.length && fmt[i] in '0'..'9') {
                                len = len * 10 + (fmt[i++] - '0')
                            }
                            if (i == fmt.length || fmt[i] != 's') format.stringErrorAt(i, "missing s after %$len")
                            ++i
                            if (type !== SignedCharType) format.stringErrorAt(percent, "%s expects char*, not ${type.pointer()}")
                            val maxLen = referenced.bound - referenced.index - 1
                            if (len > maxLen) format.stringErrorAt(percent, "%${len}s is ${len - maxLen} too long\nuse %${maxLen}s instead")
                            skipWhitespace()
                            val x = scanString(len)
                            var obj = referenced
                            for (c in x) {
                                obj.assign(Value.signedChar(c))
                                obj += 1
                            }
                            obj.assign(Value.NUL)
                        }

                        else -> format.stringErrorAt(percent, "illegal conversion specifier %${fmt[i - 1]}")
                    }
                    after?.invoke()
                }

                else -> if (getchar() != fmt[i - 1]) {
                    unget()
                    return a
                }
            }
        }
        return arguments.size
    }

    private fun skipWhitespace() {
        @Suppress("ControlFlowWithEmptyBody")
        while (getchar().isWhitespace()) {
        }
        unget()
    }

    private fun scanInt(): Int? {
        var c = getchar()
        var sign = 1
        if (c == '-') {
            sign = -1
            c = getchar()
        }
        if (c !in '0'..'9') return null
        var x = c - '0'
        while (getchar() in '0'..'9') {
            x = x * 10 + (current - '0')
        }
        unget()
        return sign * x
    }

    private fun scanDouble(): Double? {
        var c = getchar()
        var sign = 1
        if (c == '-') {
            sign = -1
            c = getchar()
        }
        if (c !in '0'..'9') return null
        var x = (c - '0').toDouble()
        while (getchar() in '0'..'9') {
            x = x * 10 + (current - '0')
        }
        if (current == '.') {
            var decimal = 1.0
            while (getchar() in '0'..'9') {
                decimal /= 10
                x += (current - '0') * decimal
            }
        }
        unget()
        return sign * x
    }

    private fun scanString(len: Int): CharSequence {
        val sb = StringBuilder()
        while (getchar() > ' ') {
            sb.append(current)
            if (sb.length == len) return sb
        }
        unget()
        return sb
    }

    fun getText(): String {
        isDirty = false
        if (!isBlocked()) return output.toString()

        val result = StringBuilder()
        result.append(output)
        result.append(input)
        result.append('_')
        return result.toString()
    }

    fun keyTyped(x: Char) {
        when (x) {
            in '\u0020'..'\u007e' -> input.append(x)
            in '\u00a0'..'\u00ff' -> input.append(x)

            '\b' -> backspace()

            '\n' -> enter()

            '\u0004', '\u001a' -> stop()
        }
    }

    private fun backspace() {
        val len = input.length
        if (len > 0) {
            input.setLength(len - 1)
        }
    }

    private fun enter() {
        input.append('\n')
        output.append(input)
        val temp = input
        input = StringBuilder()
        blocked.set(false)
        for (x in temp) {
            queue.put(x)
        }
    }

    fun stop() {
        queue.put('\uffff')
    }

    fun getchar(): Char {
        val x: Char? = queue.poll()
        if (x != null) return remember(x)

        blocked.set(true)
        update?.invoke()
        val y: Char = queue.take()
        return remember(y)
    }

    private fun remember(x: Char): Char {
        current = x
        return x
    }

    private var current = '\u0000'

    private fun unget() {
        queue.putFirst(current)
        current = '\u0000'
    }
}


================================================
FILE: src/main/kotlin/interpreter/FlatStatements.kt
================================================
package interpreter

import syntax.lexer.Token
import syntax.lexer.missingIdentifier
import syntax.tree.DeclarationSpecifiers
import syntax.tree.Expression
import syntax.tree.NamedDeclarator

sealed class FlatStatement {
    abstract fun root(): Token

    open fun forEachSuccessor(action: (String) -> Unit) {
    }
}

sealed class TransferringControl : FlatStatement()

class Jump(val keyword: Token, val target: String) : TransferringControl() {
    override fun root(): Token = keyword

    override fun forEachSuccessor(action: (String) -> Unit) {
        action(target)
    }
}

class ImplicitContinue(val keyword: Token, val target: String) : TransferringControl() {
    override fun root(): Token = keyword

    override fun forEachSuccessor(action: (String) -> Unit) {
        action(target)
    }
}

class JumpIf(val condition: Expression, val th3n: String, val e1se: String) : TransferringControl() {
    override fun root(): Token = condition.root()

    override fun forEachSuccessor(action: (String) -> Unit) {
        action(th3n)
        action(e1se)
    }
}

object SwitchPlaceholder : TransferringControl() {
    override fun root(): Token = missingIdentifier
}

class HashSwitch(val control: Expression, val cases: HashMap<ArithmeticValue, String>, val default: String) :
    TransferringControl() {
    override fun root(): Token = control.root()

    override fun forEachSuccessor(action: (String) -> Unit) {
        cases.values.forEach { action(it) }
        action(default)
    }
}

class FlatDeclaration(val specifiers: DeclarationSpecifiers, val namedDeclarators: List<NamedDeclarator>) :
    FlatStatement() {
    override fun root(): Token = specifiers.root()
}

class FlatExpressionStatement(val expression: Expression) : FlatStatement() {
    override fun root(): Token = expression.root()
}

class FlatReturn(val r3turn: Token, val result: Expression?) : TransferringControl() {
    override fun root(): Token = r3turn
}

class FlatAssert(val condition: Expression) : FlatStatement() {
    override fun root(): Token = condition.root()
}


================================================
FILE: src/main/kotlin/interpreter/Interpreter.kt
================================================
package interpreter

import common.Diagnostic
import semantic.TypeChecker
import semantic.types.*
import syntax.lexer.Lexer
import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.parser.Parser
import syntax.parser.translationUnit
import syntax.tree.*
import java.time.LocalTime
import java.time.format.DateTimeFormatter.ISO_TIME
import java.time.temporal.ChronoUnit.SECONDS
import kotlin.math.floor
import kotlin.math.pow
import kotlin.random.Random

fun FunctionDefinition.returnType(): Type = (namedDeclarator.type as FunctionType).returnType

class Interpreter(program: String) {
    val translationUnit = Parser(Lexer(program)).translationUnit()
    val typeChecker = TypeChecker(translationUnit)

    private val functions = translationUnit.functions.associateBy(FunctionDefinition::name)

    var onMemorySet: Function1<Memory, Unit>? = null
    private var memory = Memory(emptySet(), emptyList())
        private set(value) {
            field = value
            onMemorySet?.invoke(value)
        }

    val console = Console()

    var stackDepth = 0
        private set
    private var passedAssertions = 0

    private var targetType: Type = VoidPointerType

    init {
        for (function in translationUnit.functions) {
            BuildControlFlowGraph(function)
        }
    }

    var before: Function1<Int, Unit>? = null
    var after: Function0<Unit>? = null

    fun run(cursor: Int, bottom: Int) {
        val main = translationUnit.functions.firstOrNull { it.name() == "main" }
        if (main != null) {
            if (main.returnType() !== SignedIntType || main.parameters.isNotEmpty()) {
                main.specifiers.root().error("int main() expected")
            }
            runMain(main)
        } else {
            val (beforeCursor, afterCursor) = translationUnit.functions
                .filter { it.returnType() === VoidType && it.parameters.isEmpty() }
                .partition { it.closingBrace.end < cursor }

            val entryPoint = afterCursor.firstOrNull() ?: beforeCursor.lastOrNull()
            ?: throw Diagnostic(
                bottom,
                "missing entry point, must provide one of:\n· void allNamesAreFine()\n· int  main()"
            )
            run(entryPoint)
        }
    }

    private fun runMain(main: FunctionDefinition) {
        initializeMemory(main)
        val exitCode = main.execute(emptyList()) as ArithmeticValue

        console.print("\nmain finished with exit code ${exitCode.value.toInt()}\n")
        console.update?.invoke()

        reportMemoryLeaks(main)
    }

    private fun run(entryPoint: FunctionDefinition) {
        initializeMemory(entryPoint)
        entryPoint.execute(emptyList())

        if (passedAssertions != 0) {
            val now = LocalTime.now().truncatedTo(SECONDS).format(ISO_TIME)
            console.print("\n[$now] ${entryPoint.name()}: ALL $passedAssertions assertions PASSED\n")
            console.update?.invoke()
        }
        reportMemoryLeaks(entryPoint)
    }

    private fun initializeMemory(start: FunctionDefinition) {
        val usedStringLiterals = HashSet<String>()

        val exploredFunctions = hashSetOf(start)

        val staticVariables: Map<String, NamedDeclarator> = translationUnit.declarations
            .filter { declaration -> declaration.specifiers.storageClass != TYPEDEF }
            .flatMap(Declaration::namedDeclarators)
            .filter { namedDeclarator -> namedDeclarator.offset < 0 && namedDeclarator.type.requiresStorage() }
            .associateBy { namedDeclarator -> namedDeclarator.name.text }
        val usedStaticOffsets = HashSet<Int>()

        fun explore(parent: Node) {
            parent.walkChildren({}) { node ->
                when (node) {
                    is StringLiteral -> {
                        usedStringLiterals.add(node.literal.text)
                    }

                    is Identifier -> {
                        if (node.type is FunctionType) {
                            functions[node.name.text]?.let { function ->
                                if (exploredFunctions.add(function)) {
                                    explore(function)
                                }
                            }
                        } else if (node.symbol.offset < 0) {
                            staticVariables[node.name.text]?.let { variable ->
                                if (usedStaticOffsets.add(node.symbol.offset)) {
                                    explore(variable)
                                }
                            }
                        }
                    }
                }
            }
        }
        explore(start)

        val stringLiterals = typeChecker.stringLiterals
        stringLiterals.retainAll(usedStringLiterals)

        if (usedStaticOffsets.isEmpty()) {
            memory = Memory(stringLiterals, emptyList())
        } else {
            memory = Memory(stringLiterals, staticVariables.values.map { namedDeclarator ->
                if (namedDeclarator.offset in usedStaticOffsets) namedDeclarator else namedDeclarator.hidden()
            })
            for (namedDeclarator in staticVariables.values) {
                with(namedDeclarator) {
                    if (declarator is Declarator.Initialized && offset in usedStaticOffsets) {
                        initialize(type, declarator.init, memory.staticVariables, offset + Int.MIN_VALUE)
                    } else {
                        defaultInitialize(type, memory.staticVariables, offset + Int.MIN_VALUE)
                    }
                }
            }
        }
    }

    private fun reportMemoryLeaks(function: FunctionDefinition) {
        if (memory.heap.isNotEmpty()) {
            function.closingBrace.error("${memory.heap.size} missing free calls")
        }
    }

    private fun FunctionDefinition.execute(arguments: List<Value>): Value {
        ++stackDepth
        try {
            val stackFrame = Segment(stackFrameType)
            memory.stack.add(stackFrame)
            for ((param, arg) in parameters.zip(arguments)) {
                param.type.cast(arg).store(stackFrame, param.offset)
            }
            after?.invoke()

            var basicBlock = controlFlowGraph["0"]!!.getStatements()
            var pc = 0
            while (pc != basicBlock.size) {
                with(basicBlock[pc++]) {
                    when (this) {
                        is Jump -> {
                            basicBlock = controlFlowGraph[target]!!.getStatements()
                            pc = 0
                        }

                        is ImplicitContinue -> {
                            basicBlock = controlFlowGraph[target]!!.getStatements()
                            pc = 0
                        }

                        is JumpIf -> {
                            val target = if (condition.delayedCondition()) th3n else e1se
                            basicBlock = controlFlowGraph[target]!!.getStatements()
                            pc = 0
                        }

                        is HashSwitch -> {
                            val target = cases[(control.delayed() as ArithmeticValue).integralPromotions()] ?: default
                            basicBlock = controlFlowGraph[target]!!.getStatements()
                            pc = 0
                        }

                        is FlatDeclaration -> {
                            for (namedDeclarator in namedDeclarators) {
                                with(namedDeclarator) {
                                    if (declarator is Declarator.Initialized && offset >= 0) {
                                        before?.invoke(name.start)
                                        initialize(type, declarator.init, stackFrame, offset)
                                        after?.invoke()
                                    }
                                }
                            }
                        }

                        is FlatExpressionStatement -> {
                            expression.delayed()
                        }

                        is FlatReturn -> {
                            if (result == null) {
                                before?.invoke(r3turn.start)
                                after?.invoke()
                            } else {
                                targetType = this@execute.returnType()
                                return targetType.cast(result.delayed()).also { memory.popStackFrameUnlessEntryPoint() }
                            }
                        }

                        is FlatAssert -> {
                            if (condition is RelationalEquality && condition.right.type is ArithmeticType) {
                                val left = condition.left.delayed()
                                val right = condition.right.evaluate()
                                if (relationalEquality(
                                        left as ArithmeticValue,
                                        condition.operator,
                                        right as ArithmeticValue
                                    ).isFalse()
                                ) {
                                    val leftShow = left.show()
                                    condition.root().error(
                                        " $leftShow ${condition.operator} ${right.show()} ",
                                        -leftShow.length - 2
                                    )
                                }
                            } else if (!condition.delayedCondition()) {
                                condition.root().error("assertion failed")
                            }
                            ++passedAssertions
                        }

                        else -> error("no execute for $this")
                    }
                }
            }

            before?.invoke(closingBrace.start)

            if (returnType() !== VoidType) {
                throw Diagnostic(closingBrace.start, "missing return statement")
            }
            memory.popStackFrameUnlessEntryPoint()
            return VoidValue
        } finally {
            --stackDepth
        }
    }

    private fun initialize(qualified: Type, init: Initializer, segment: Segment, start: Int): Int {
        val type = qualified.unqualified()
        when (init) {
            is ExpressionInitializer -> {
                return if (init.expression is StringLiteral && type is ArrayType && type.elementType == SignedCharType) {
                    val str = init.expression.literal.text
                    for ((i, c) in str.withIndex()) {
                        segment[start + i] = Value.signedChar(c)
                    }
                    for (i in str.length until type.size) {
                        segment[start + i] = Value.NUL
                    }
                    start + type.size
                } else {
                    targetType = type
                    val value = type.cast(init.expression.evaluate())
                    value.store(segment, start)
                }
            }

            is InitializerList -> {
                when (type) {
                    is ArrayType -> return init.list.fold(start) { offset, initializer ->
                        initialize(type.elementType, initializer, segment, offset)
                    }

                    is StructType -> return type.members.zip(init.list).fold(start) { offset, memberInitializer ->
                        initialize(memberInitializer.first.type, memberInitializer.second, segment, offset)
                    }
                }
            }
        }
        error("no init for $init")
    }

    private fun defaultInitialize(qualified: Type, segment: Segment, start: Int): Int {
        val type = qualified.unqualified()
        when (type) {
            is ArithmeticType -> {
                segment[start] = type.defaultValue
            }

            is ArrayType -> {
                (0 until type.size).fold(start) { offset, _ ->
                    defaultInitialize(type.elementType, segment, offset)
                }
            }

            is StructType -> {
                type.members.fold(start) { offset, member ->
                    defaultInitialize(member.type, segment, offset)
                }
            }
        }
        return start + type.count()
    }

    private fun Expression.delayed(): Value {
        before?.invoke(root().start)
        val result = evaluate()
        after?.invoke()
        return result
    }

    private fun Expression.delayedCondition(): Boolean {
        return (delayed() as ArithmeticValue).isTrue()
    }

    private fun Expression.locate(): Object {
        return when (this) {
            is StringLiteral -> {
                memory.stringObjects[literal.text]!!
            }

            is Identifier -> {
                memory.makeObject(symbol)
            }

            is Subscript -> {
                val left = left.evaluate()
                val right = right.evaluate()
                if (left is PointerValue && right is ArithmeticValue) {
                    left.referenced.checkReferable() + right.value.toInt()
                } else if (left is ArithmeticValue && right is PointerValue) {
                    right.referenced.checkReferable() + left.value.toInt()
                } else {
                    error("no locate for $this")
                }
            }

            is DirectMemberAccess -> {
                val struct = left.locate()

                val type = struct.type.unqualified() as StructType
                val member = type.member(right)
                Object(struct.segment, struct.offset + member!!.offset, member.type, 0, 1)
            }

            is IndirectMemberAccess -> {
                val pointer = left.evaluate() as PointerValue
                val struct = pointer.referenced

                val type = struct.type.unqualified() as StructType
                val member = type.member(right)
                Object(struct.segment, struct.offset + member!!.offset, member.type, 0, 1)
            }

            is Dereference -> {
                val pointer = operand.evaluate() as PointerValue
                pointer.referenced.checkReferable()
            }

            else -> error("no locate for $this")
        }
    }

    private fun Expression.evaluate(): Value {
        value?.let { return it }
        return when (this) {
            is Identifier -> {
                val symbolType = symbol.type
                if (symbolType is FunctionType) {
                    FunctionDesignator(symbol.name, symbolType)
                } else {
                    locate().evaluate()
                }
            }

            is PrintfCall -> {
                Value.signedInt(console.printf(format, arguments.map { it.evaluate() }))
            }

            is ScanfCall -> {
                Value.signedInt(console.scanf(format, arguments.map { it.evaluate() }, after))
            }

            is Postfix -> {
                val obj = operand.locate()
                val oldValue = obj.evaluate()
                val newValue = if (oldValue is ArithmeticValue) {
                    val result = if (operator.kind == PLUS_PLUS) oldValue + Value.ONE else oldValue - Value.ONE
                    oldValue.type.cast(result)
                } else if (oldValue is PointerValue) {
                    if (operator.kind == PLUS_PLUS) oldValue + 1 else oldValue - 1
                } else {
                    error("no evaluate for $this")
                }
                obj.assign(newValue)
                oldValue
            }

            is FunctionCall -> {
                val func = (function.evaluate().decayed() as FunctionPointerValue).designator
                val name = func.functionName
                val definition = functions[name.text]
                if (definition != null) {
                    val evaluatedArguments = definition.parameters.zip(arguments).map {
                        targetType = it.first.type
                        it.second.evaluate()
                    }
                    return definition.execute(evaluatedArguments)
                }
                when (name.text) {
                    "pow" -> {
                        val base = (arguments[0].evaluate() as ArithmeticValue).value
                        val exponent = (arguments[1].evaluate() as ArithmeticValue).value
                        return ArithmeticValue(base.pow(exponent), DoubleType)
                    }

                    "time" -> {
                        return ArithmeticValue(floor(System.currentTimeMillis() / 1000.0), UnsignedIntType)
                    }

                    "puts" -> {
                        console.puts(arguments[0].evaluate() as PointerValue)
                        return VoidValue
                    }

                    "putchar" -> {
                        val arg = arguments[0].evaluate() as ArithmeticValue
                        console.putchar(arg.value.toLong().toInt().and(0xff).toChar())
                        return VoidValue
                    }

                    "getchar" -> {
                        return Value.signedInt(console.getchar().code.toByte().toInt())
                    }

                    "malloc" -> {
                        return allocate(function, arguments[0], memory::malloc, memory::malloc)
                    }

                    "free" -> {
                        memory.free(arguments[0].evaluate() as PointerValue)
                        return VoidValue
                    }

                    "realloc" -> {
                        val pointer = arguments[0].evaluate() as PointerValue
                        return allocate(
                            function,
                            arguments[1],
                            // DO NOT REFACTOR: The lambdas call different realloc overloads!
                            { type -> memory.realloc(pointer, type) },
                            { type -> memory.realloc(pointer, type) }
                        )
                    }

                    "memswap" -> {
                        val p = arguments[0].evaluate() as PointerValue
                        val q = arguments[1].evaluate() as PointerValue
                        if (p.referenced.type != q.referenced.type) {
                            error("${p.referenced.type} != ${q.referenced.type}")
                        }
                        val size = (arguments[2].evaluate() as ArithmeticValue).value.toInt()
                        val actualSize = p.referenced.type.sizeof()
                        if (size != actualSize) {
                            error("element type ${p.referenced.type} has size $actualSize, not $size")
                        }
                        swap(p, q)
                        return VoidValue
                    }

                    "qsort" -> {
                        val base = arguments[0].evaluate() as PointerValue
                        val count = (arguments[1].evaluate() as ArithmeticValue).value.toInt()
                        val maxCount = base.referenced.bound - base.referenced.index
                        if (count > maxCount) {
                            error("There are only $maxCount elements in the array, not $count")
                        }
                        val size = (arguments[2].evaluate() as ArithmeticValue).value.toInt()
                        val actualSize = base.referenced.type.sizeof()
                        if (size != actualSize) {
                            error("element type ${base.referenced.type} has size $actualSize, not $size")
                        }
                        val comp = arguments[3].evaluate().decayed() as FunctionPointerValue
                        qsort(base, count, functions[comp.designator.functionName.text]!!)
                        return VoidValue
                    }

                    "bsearch" -> {
                        val key = arguments[0].evaluate() as PointerValue
                        val base = arguments[1].evaluate() as PointerValue
                        val count = (arguments[2].evaluate() as ArithmeticValue).value.toInt()
                        val maxCount = base.referenced.bound - base.referenced.index
                        if (count > maxCount) {
                            error("There are only $maxCount elements in the array, not $count")
                        }
                        val size = (arguments[3].evaluate() as ArithmeticValue).value.toInt()
                        val actualSize = base.referenced.type.sizeof()
                        if (size != actualSize) {
                            error("element type ${base.referenced.type} has size $actualSize, not $size")
                        }
                        val comp = arguments[4].evaluate().decayed() as FunctionPointerValue
                        return bsearch(key, base, count, functions[comp.designator.functionName.text]!!)
                    }

                    "strlen" -> {
                        val s = arguments[0].evaluate() as PointerValue
                        return strlen(s, 0)
                    }

                    "strcmp" -> {
                        val s = arguments[0].evaluate() as PointerValue
                        val t = arguments[1].evaluate() as PointerValue
                        return strcmp(s, t)
                    }

                    else -> error("undefined function $name")
                }
            }

            is Prefix -> {
                val obj = operand.locate()
                val oldValue = obj.evaluate()
                val newValue = if (oldValue is ArithmeticValue) {
                    val result = if (operator.kind == PLUS_PLUS) oldValue + Value.ONE else oldValue - Value.ONE
                    oldValue.type.cast(result)
                } else if (oldValue is PointerValue) {
                    if (operator.kind == PLUS_PLUS) oldValue + 1 else oldValue - 1
                } else {
                    error("no evaluate for $this")
                }
                obj.assign(newValue)
                newValue
            }

            is Reference -> {
                if (operand.type is FunctionType) {
                    FunctionPointerValue(operand.evaluate() as FunctionDesignator)
                } else {
                    PointerValue(operand.locate())
                }
            }

            is Dereference -> {
                if ((operand.type.decayed() as PointerType).referencedType is FunctionType) {
                    (operand.evaluate().decayed() as FunctionPointerValue).designator
                } else {
                    locate().evaluate()
                }
            }

            is UnaryPlus -> {
                unaryPlus(operand.evaluate())
            }

            is UnaryMinus -> {
                unaryMinus(operand.evaluate())
            }

            is BitwiseNot -> {
                bitwiseNot(operand.evaluate())
            }

            is LogicalNot -> {
                logicalNot(operand.evaluate())
            }

            is Multiplicative -> {
                multiplicative(left.evaluate(), operator, right.evaluate())
            }

            is Plus -> {
                val left = left.evaluate()
                val right = right.evaluate()
                if (left is PointerValue && right is ArithmeticValue) {
                    pointerPlus(left, this.left.type, right)
                } else if (left is ArithmeticValue && right is PointerValue) {
                    pointerPlus(right, this.right.type, left)
                } else {
                    plus(left, right)
                }
            }

            is Minus -> {
                val type = this.left.type
                val left = left.evaluate()
                val right = right.evaluate()
                if (left is PointerValue && right is ArithmeticValue) {
                    pointerMinus(left, type, right)
                } else if (left is PointerValue && right is PointerValue) {
                    if (type is VoidPointerType || type is ConstVoidPointerType) {
                        Value.signedInt((left - right) * left.referenced.type.sizeof())
                    } else {
                        Value.signedInt(left - right)
                    }
                } else {
                    minus(left, right)
                }
            }

            is Shift -> {
                shift(left.evaluate(), operator, right.evaluate(), type)
            }

            is RelationalEquality -> {
                val left = left.evaluate()
                val right = right.evaluate()
                if (left is PointerValue && right is PointerValue) {
                    Value.truth(
                        when (operator.kind) {
                            LESS -> left.less(right)
                            MORE -> right.less(left)
                            LESS_EQUAL -> !right.less(left)
                            MORE_EQUAL -> !left.less(right)
                            EQUAL_EQUAL -> left.equal(right)
                            BANG_EQUAL -> !left.equal(right)

                            else -> error("no evaluate for $this")
                        }
                    )
                } else {
                    relationalEquality(left as ArithmeticValue, operator, right as ArithmeticValue)
                }
            }

            is Bitwise -> {
                bitwise(left.evaluate(), operator, right.evaluate(), type)
            }

            is Logical -> {
                val left = left.evaluate() as ArithmeticValue
                when (operator.kind) {
                    AMPERSAND_AMPERSAND -> {
                        if (left.isFalse()) Value.ZERO
                        else (right.evaluate() as ArithmeticValue).normalizeBool()
                    }

                    BAR_BAR -> {
                        if (left.isTrue()) Value.ONE
                        else (right.evaluate() as ArithmeticValue).normalizeBool()
                    }

                    else -> error("no evaluate for $this")
                }
            }

            is Conditional -> {
                val condition = condition.evaluate() as ArithmeticValue
                val result = if (condition.isTrue()) th3n.delayed() else e1se.delayed()
                type.cast(result)
            }

            is Cast -> {
                type.cast(operand.evaluate())
            }

            is Assignment -> {
                targetType = left.type
                val value = left.type.cast(right.evaluate())
                val obj = left.locate()
                obj.preventSentinelAccess()
                value.store(obj.segment, obj.offset)
                value
            }

            is PlusAssignment -> {
                val target = left.locate()
                val left = target.evaluate()
                val right = right.evaluate() as ArithmeticValue
                val value = when (left) {
                    is ArithmeticValue -> target.type.cast(left + right)

                    is PointerValue -> pointerPlus(left, target.type, right)

                    else -> error("no evaluate for $this")
                }
                target.assign(value)
                value
            }

            is MinusAssignment -> {
                val target = left.locate()
                val left = target.evaluate()
                val right = right.evaluate() as ArithmeticValue
                val value = when (left) {
                    is ArithmeticValue -> target.type.cast(left - right)

                    is PointerValue -> pointerMinus(left, target.type, right)

                    else -> error("no evaluate for $this")
                }
                target.assign(value)
                value
            }

            is Comma -> {
                left.evaluate()
                right.evaluate()
            }

            else -> {
                if (!isLocator) error("no evaluate for $this")
                locate().evaluate()
            }
        }
    }

    private fun Binary.pointerPlus(pointer: PointerValue, type: Type, arithmetic: ArithmeticValue): Value {
        val delta = arithmetic.value.toInt()
        return if (type is VoidPointerType || type is ConstVoidPointerType) {
            val sizeof = pointer.referenced.type.sizeof()
            if (delta % sizeof != 0) {
                this.root().error("$delta is not a multiple of $sizeof")
            }
            pointer + delta / sizeof
        } else {
            pointer + delta
        }
    }

    private fun Binary.pointerMinus(pointer: PointerValue, type: Type, arithmetic: ArithmeticValue): Value {
        val delta = arithmetic.value.toInt()
        return if (type is VoidPointerType || type is ConstVoidPointerType) {
            val sizeof = pointer.referenced.type.sizeof()
            if (delta % sizeof != 0) {
                this.root().error("$delta is not a multiple of $sizeof")
            }
            pointer - delta / sizeof
        } else {
            pointer - delta
        }
    }

    private fun qsort(base: PointerValue, count: Int, comp: FunctionDefinition) {
        // Programming Pearls
        // 11.3 Better Quicksorts
        fun q(l: Int, u: Int) {
            if (l < u) {
                val pivot = base + l
                swap(pivot, base + Random.nextInt(l, u + 1))
                var i = l
                var j = u + 1
                while (true) {
                    do ++i while (i <= u && (comp.execute(listOf(base + i, pivot)) as ArithmeticValue).value < 0)
                    do --j while (/*     */ (comp.execute(listOf(base + j, pivot)) as ArithmeticValue).value > 0)
                    if (i > j) break
                    swap(base + i, base + j)
                }
                swap(pivot, base + j)
                q(l, j - 1)
                q(j + 1, u)
            }
        }
        q(0, count - 1)
    }

    private fun swap(p: PointerValue, q: PointerValue) {
        val a = p.referenced
        val b = q.referenced
        for (i in 0 until a.type.count()) {
            val x = a.segment[a.offset + i]
            val y = b.segment[b.offset + i]
            a.segment[a.offset + i] = y
            b.segment[b.offset + i] = x
        }
    }

    private fun bsearch(key: PointerValue, base: PointerValue, count: Int, comp: FunctionDefinition): Value {
        var left = 0
        var right = count
        while (left < right) {
            val middle = (left + right).ushr(1)
            val comparison = (comp.execute(listOf(key, base + middle)) as ArithmeticValue).value
            when {
                comparison < 0 -> right = middle

                comparison > 0 -> left = middle + 1

                else -> return base + middle
            }
        }
        return base + count
    }

    private tailrec fun strlen(s: PointerValue, len: Int): ArithmeticValue {
        val c = s.referenced.evaluate() as ArithmeticValue
        return when {
            (c == Value.NUL) -> Value.unsignedChar(len)

            else -> strlen(s + 1, len + 1)
        }
    }

    private tailrec fun strcmp(s: PointerValue, t: PointerValue): ArithmeticValue {
        val c = s.referenced.evaluate() as ArithmeticValue
        val d = t.referenced.evaluate() as ArithmeticValue
        return when {
            (c != d) -> (c - d)

            (c == Value.NUL) -> Value.ZERO

            else -> strcmp(s + 1, t + 1)
        }
    }

    private fun allocate(
        function: Expression,
        size: Expression,
        one: (Type) -> PointerValue,
        many: (ArrayType) -> PointerValue
    ): PointerValue {
        if (targetType === VoidPointerType) {
            function.root().error("cannot infer desired type to allocate via void*")
        }
        val elementType = (targetType as PointerType).referencedType
        val elementSize = elementType.sizeof()
        val requestedBytes = (size.evaluate() as ArithmeticValue).value.toInt()
        val arraySize = requestedBytes / elementSize
        if (arraySize * elementSize != requestedBytes) {
            size.root()
                .error("$requestedBytes is not a multiple of $elementSize. Did you forget to multiply by sizeof(element type)?")
        }
        return if (arraySize == 1) one(elementType)
        else many(ArrayType(arraySize, elementType))
    }
}

fun unaryPlus(x: Value): Value {
    val a = x as ArithmeticValue
    return Value.ZERO + a
}

fun unaryMinus(x: Value): Value {
    val a = x as ArithmeticValue
    return Value.ZERO - a
}

fun bitwiseNot(x: Value): Value {
    val a = x as ArithmeticValue
    return Value.MINUS_ONE - a
}

fun logicalNot(x: Value): Value {
    val a = x as ArithmeticValue
    return Value.truth(a.isFalse())
}

fun multiplicative(x: Value, operator: Token, y: Value): Value {
    val a = x as ArithmeticValue
    val b = y as ArithmeticValue
    return when (operator.kind) {
        ASTERISK -> a * b
        SLASH -> a / b
        PERCENT -> a % b

        else -> error("no evaluate for $operator")
    }
}

fun plus(x: Value, y: Value): Value {
    val a = x as ArithmeticValue
    val b = y as ArithmeticValue
    return a + b
}

fun minus(x: Value, y: Value): Value {
    val a = x as ArithmeticValue
    val b = y as ArithmeticValue
    return a - b
}

fun shift(x: Value, operator: Token, y: Value, type: Type): Value {
    val a = (x as ArithmeticValue).integralPromotions().value.toLong().toInt()
    val b = (y as ArithmeticValue).integralPromotions().value.toLong().toInt()
    val bits = when {
        operator.kind == LESS_LESS -> log(a, "<< ", b, a.shl(b))

        x.type === UnsignedIntType -> log(a, ">>>", b, a.ushr(b))

        else -> log(a, ">> ", b, a.shr(b))
    }
    return type.cast(Value.signedInt(bits))
}

fun relationalEquality(x: ArithmeticValue, operator: Token, y: ArithmeticValue): ArithmeticValue {
    val commonType = x.type.usualArithmeticConversions(y.type)
    val a = commonType.cast(x).value
    val b = commonType.cast(y).value
    return Value.truth(
        when (operator.kind) {
            LESS -> a < b
            MORE -> a > b
            LESS_EQUAL -> a <= b
            MORE_EQUAL -> a >= b
            EQUAL_EQUAL -> a == b
            BANG_EQUAL -> a != b

            else -> error("no evaluate for $operator")
        }
    )
}

fun bitwise(x: Value, operator: Token, y: Value, type: Type): Value {
    val a = (x as ArithmeticValue).value.toLong().toInt()
    val b = (y as ArithmeticValue).value.toLong().toInt()
    val result = when (operator.kind) {
        AMPERSAND -> log(a, " & ", b, a.and(b))
        CARET -> log(a, " ^ ", b, a.xor(b))
        BAR -> log(a, " | ", b, a.or(b))

        else -> error("no evaluate for $operator")
    }
    return type.cast(Value.signedInt(result))
}

private fun log(x: Int, op: String, y: Int, z: Int): Int {
    println("""
   ${x.toBinaryString32()} $x
$op${y.toBinaryString32()} $y
 = ${z.toBinaryString32()} $z""")
    return z
}

private fun Int.toBinaryString32(): String {
    return nib(28) + nib(24) + " " + nib(20) + nib(16) + " " + nib(12) + nib(8) + " " + nib(4) + nib(0)
}

private fun Int.nib(pos: Int): String {
    return NIBBLES[this.ushr(pos).and(15)]
}

private val NIBBLES = arrayOf(
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111",
)


================================================
FILE: src/main/kotlin/interpreter/Memory.kt
================================================
package interpreter

import semantic.Symbol
import semantic.types.ArrayType
import semantic.types.SignedCharType
import semantic.types.StructType
import semantic.types.Type
import syntax.lexer.fakeIdentifier
import syntax.lexer.missingIdentifier
import syntax.tree.NamedDeclarator
import kotlin.math.min

fun Iterable<String>.synthesizeStringConstantsType(): StructType {
    val symbols = ArrayList<Symbol>()
    val type = StructType(fakeIdentifier("string literals"), symbols)
    var offset = 0
    for (str in this) {
        val size = str.length + 1
        symbols.add(Symbol(missingIdentifier, ArrayType(size, SignedCharType), offset))
        offset += size
    }
    return type
}

fun Iterable<NamedDeclarator>.synthesizeStaticVariablesType(): StructType {
    val symbols = ArrayList<Symbol>()
    val type = StructType(fakeIdentifier("static variables"), symbols)
    for (namedDeclarator in this) {
        with(namedDeclarator) {
            symbols.add(Symbol(name, this.type, offset))
        }
    }
    return type
}

class Memory(stringLiterals: Iterable<String>, variables: Iterable<NamedDeclarator>) {
    val stringObjects = HashMap<String, Object>()

    val stringConstants = Segment(stringLiterals.synthesizeStringConstantsType())
    val staticVariables = Segment(variables.synthesizeStaticVariablesType())
    val stack = ArrayList<Segment>()
    val heap = ArrayList<Segment>()

    init {
        var stringOffset = 0
        for (stringLiteral in stringLiterals) {
            stringObjects[stringLiteral] =
                Object(stringConstants, stringOffset, ArrayType(stringLiteral.length + 1, SignedCharType), 0, 1)
            for (x in stringLiteral) {
                stringConstants[stringOffset++] = Value.signedChar(x)
            }
            stringConstants[stringOffset++] = Value.NUL
        }
        stringConstants.readOnlyErrorMessage = "attempt to modify a string literal"
    }

    fun makeObject(symbol: Symbol): Object {
        return with(symbol) {
            if (offset < 0) {
                Object(staticVariables, offset + Int.MIN_VALUE, type, 0, 1)
            } else {
                Object(stack.last(), offset, type, 0, 1)
            }
        }
    }

    fun popStackFrameUnlessEntryPoint() {
        if (stack.size > 1) {
            stack.removeAt(stack.lastIndex).kill()
        }
    }

    fun malloc(type: Type): PointerValue {
        val segment = Segment(type)
        heap.add(segment)
        return PointerValue(Object(segment, 0, type, 0, 1))
    }

    fun malloc(arrayType: ArrayType): PointerValue {
        val segment = Segment(arrayType)
        heap.add(segment)
        return PointerValue(Object(segment, 0, arrayType.elementType, 0, arrayType.size))
    }

    fun free(pointer: PointerValue) {
        val obj = pointer.referenced
        val segment = obj.segment

        if (!segment.alive) throw AssertionError("dangling pointer")
        if (segment !in heap) throw AssertionError("free on non-heap segment")
        if (obj.offset != 0) throw AssertionError("free in the middle of segment")

        segment.kill()
        heap.remove(segment)
    }

    fun realloc(pointer: PointerValue, type: Type): PointerValue {
        val oldSegment = pointer.referenced.segment
        val newSegment = Segment(type)
        val smallerCount = min(oldSegment.count(), newSegment.count())
        for (i in 0 until smallerCount) {
            newSegment[i] = oldSegment[i]
        }
        free(pointer)
        heap.add(newSegment)
        return PointerValue(Object(newSegment, 0, type, 0, 1))
    }

    fun realloc(pointer: PointerValue, arrayType: ArrayType): PointerValue {
        val oldSegment = pointer.referenced.segment
        val newSegment = Segment(arrayType)
        val smallerCount = min(oldSegment.count(), newSegment.count())
        for (i in 0 until smallerCount) {
            newSegment[i] = oldSegment[i]
        }
        free(pointer)
        heap.add(newSegment)
        return PointerValue(Object(newSegment, 0, arrayType.elementType, 0, arrayType.size))
    }
}


================================================
FILE: src/main/kotlin/interpreter/Segment.kt
================================================
package interpreter

import semantic.types.Type

class Segment(val type: Type) {
    private val memory: MutableList<Value> = MutableList(type.count()) { IndeterminateValue }
    var readOnlyErrorMessage: String? = null

    var alive = true
        private set

    fun kill() {
        assert(alive)
        alive = false
    }

    fun checkAlive() {
        if (!alive) throw AssertionError("dangling pointer")
    }

    fun count(): Int {
        return type.count()
    }

    operator fun get(offset: Int): Value {
        checkAlive()
        return memory[offset]
    }

    operator fun set(offset: Int, newValue: Value) {
        checkAlive()
        if (readOnlyErrorMessage != null) throw AssertionError(readOnlyErrorMessage)
        val oldValue = memory[offset]
        assert(oldValue === IndeterminateValue || oldValue.type() == newValue.type()) {
            "$oldValue === $IndeterminateValue || ${oldValue.type()} == ${newValue.type()}"
        }
        memory[offset] = newValue
    }

    operator fun set(obj: Object, newValue: Value) {
        set(obj.offset, newValue)
    }

    fun replace(offset: Int, count: Int, source: Segment, sourceOffset: Int) {
        checkAlive()
        if (readOnlyErrorMessage != null) throw AssertionError(readOnlyErrorMessage)
        source.checkAlive()

        val sourceMemory = source.memory
        for (i in 0 until count) {
            memory[offset + i] = sourceMemory[sourceOffset + i]
        }
    }

    companion object {
        private var state = System.currentTimeMillis().toInt()

        // generates 4,294,967,296 unique addresses before repeating
        fun randomAddress(): Int {
            state = state * 214013 + 2531011
            return state
        }
    }

    val address = randomAddress()
}


================================================
FILE: src/main/kotlin/interpreter/Value.kt
================================================
package interpreter

import semantic.types.*
import syntax.lexer.Token

data class Object(val segment: Segment, val offset: Int, val type: Type, val index: Int, val bound: Int) {
    init {
        if (index < 0) throw AssertionError("negative index $index")
        if (index > bound) throw AssertionError("index $index out of bounds $bound")
    }

    fun address(): Int {
        val hi = segment.address
        val lo = segment.type.sizeof(offset)
        return hi.shl(16) + lo
    }

    fun isSentinel(): Boolean = (index == bound)

    operator fun plus(delta: Int): Object = copy(offset = offset + delta * type.count(), index = index + delta)

    operator fun minus(delta: Int): Object = copy(offset = offset - delta * type.count(), index = index - delta)

    fun preventSentinelAccess() {
        if (isSentinel()) throw AssertionError("index $index out of bounds $bound")
    }

    fun checkReferable(): Object {
        segment.checkAlive()
        return this
    }

    fun isReferable(): Boolean {
        return segment.alive
    }

    fun evaluate(): Value {
        return when (val type = this.type.unqualified()) {
            is ArrayType -> {
                // array-to-pointer decay
                PointerValue(copy(type = type.elementType, index = 0, bound = type.size))
            }

            is StructType -> {
                // structs are not values, they must be preserved as objects
                StructPseudoValue(this)
            }

            else -> {
                preventSentinelAccess()
                val result = segment[offset]
                if (result == IndeterminateValue) throw AssertionError("read from uninitialized variable")
                result
            }
        }
    }

    fun assign(newValue: Value) {
        preventSentinelAccess()
        segment[this] = newValue
    }
}

interface Value {
    fun type(): Type

    fun show(): String

    fun decayed(): Value = this

    fun store(segment: Segment, offset: Int): Int {
        segment[offset] = this
        return offset + 1
    }

    companion object {
        fun signedChar(x: Char): ArithmeticValue = ArithmeticValue(x.code.toByte().toDouble(), SignedCharType)
        fun unsignedChar(x: Int): ArithmeticValue = ArithmeticValue(x.toDouble(), UnsignedCharType)

        fun signedShort(x: Int): ArithmeticValue = ArithmeticValue(x.toDouble(), SignedShortType)
        fun unsignedShort(x: Int): ArithmeticValue = ArithmeticValue(x.toDouble(), UnsignedShortType)

        fun signedInt(x: Int): ArithmeticValue = ArithmeticValue(x.toDouble(), SignedIntType)
        fun unsignedInt(x: Int): ArithmeticValue =
            ArithmeticValue(x.toLong().and(0xffffffff).toDouble(), UnsignedIntType)

        fun float(x: Float): ArithmeticValue = ArithmeticValue(x.toDouble(), FloatType)
        fun double(x: Double): ArithmeticValue = ArithmeticValue(x, DoubleType)

        val ONE = signedInt(1)
        val NUL = signedChar('\u0000')
        val ZERO = signedInt(0)
        val MINUS_ONE = signedInt(-1)

        fun truth(x: Boolean): ArithmeticValue = if (x) ONE else ZERO
    }
}

object VoidValue : Value {
    override fun type(): Type = VoidType

    override fun show(): String = "void"
}

data class ArithmeticValue(val value: Double, val type: ArithmeticType) : Value {
    override fun type(): Type = type

    override fun show(): String = type.show(value)

    operator fun plus(that: ArithmeticValue): ArithmeticValue =
        ArithmeticValue(value + that.value, type.usualArithmeticConversions(that.type)).trim()

    operator fun minus(that: ArithmeticValue): ArithmeticValue =
        ArithmeticValue(value - that.value, type.usualArithmeticConversions(that.type)).trim()

    operator fun times(that: ArithmeticValue): ArithmeticValue =
        ArithmeticValue(value * that.value, type.usualArithmeticConversions(that.type)).trim()

    operator fun div(that: ArithmeticValue): ArithmeticValue =
        ArithmeticValue(value / that.value, type.usualArithmeticConversions(that.type)).trim()

    operator fun rem(that: ArithmeticValue): ArithmeticValue =
        ArithmeticValue(value % that.value, type.usualArithmeticConversions(that.type)).trim()

    private fun trim(): ArithmeticValue = ArithmeticValue(type.trim(value), type)

    fun integralPromotions(): ArithmeticValue = type.integralPromotions().cast(this)

    fun isFalse(): Boolean = (value == 0.0)

    fun isTrue(): Boolean = (value != 0.0)

    fun normalizeBool(): ArithmeticValue = if (value == 0.0 || value == 1.0) this else Value.ONE
}

data class PointerValue(val referenced: Object) : Value {
    override fun type(): Type = PointerType(referenced.type)

    override fun show(): String = "%08x".format(referenced.address())

    operator fun plus(delta: Int): PointerValue = PointerValue(referenced.checkReferable() + delta)

    operator fun minus(delta: Int): PointerValue = PointerValue(referenced.checkReferable() - delta)

    operator fun minus(base: PointerValue): Int {
        if (referenced.segment != base.referenced.segment) throw AssertionError("subtract across segments")
        return (referenced.offset - base.referenced.offset) / referenced.type.count()
    }

    infix fun equal(that: PointerValue): Boolean {
        return this.referenced.segment === that.referenced.segment &&
                this.referenced.offset == that.referenced.offset
    }

    infix fun less(that: PointerValue): Boolean {
        if (this.referenced.segment !== that.referenced.segment) throw AssertionError("compare across segments")
        return this.referenced.offset < that.referenced.offset
    }
}

data class FunctionDesignator(val functionName: Token, val functionType: FunctionType) : Value {
    override fun type(): Type = functionType

    override fun show(): String = error("show on function designator")

    override fun decayed(): Value = FunctionPointerValue(this)
}

data class FunctionPointerValue(val designator: FunctionDesignator) : Value {
    override fun type(): Type = PointerType(designator.functionType)

    override fun show(): String = "&${designator.functionName}"
}

object IndeterminateValue : Value {
    override fun type(): Type = throw AssertionError("indeterminate value has no type")

    override fun show(): String = ""
}

data class StructPseudoValue(val struct: Object) : Value {
    override fun type(): Type = struct.type

    override fun show(): String = "SPV"

    override fun store(segment: Segment, offset: Int): Int {
        val count = struct.type.count()
        segment.replace(offset, count, struct.segment, struct.offset)
        return offset + count
    }
}


================================================
FILE: src/main/kotlin/semantic/Linter.kt
================================================
package semantic

import interpreter.ArithmeticValue
import interpreter.BasicBlock
import interpreter.ImplicitContinue
import interpreter.returnType
import semantic.types.FunctionType
import semantic.types.VoidType
import semantic.types.isString
import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.tree.*

class Linter(val translationUnit: TranslationUnit) : LinterBase() {
    init {
        detectLowHangingFruit()
        detectUnusedVariables()
        detectMissingReturns()
        detectUnreachableCode()
    }

    private fun detectLowHangingFruit() {
        translationUnit.walk({}) {
            when (it) {
                is Comma -> {
                    it.left.detectOperatorWithoutEffect()
                }

                is ExpressionStatement -> {
                    if (it.expression is FunctionCall) {
                        if (it.expression.type !== VoidType) {
                            it.expression.warn("ignored function result")
                        }
                    } else {
                        it.expression.detectOperatorWithoutEffect()
                    }
                }

                is IfThenElse -> {
                    it.condition.detectSuspiciousCondition()
                }

                is While -> {
                    it.condition.detectSuspiciousCondition()
                }

                is Do -> {
                    it.condition.detectSuspiciousCondition()
                }

                is For -> {
                    it.condition?.detectSuspiciousCondition()
                    it.update?.detectOperatorWithoutEffect()
                }

                is Assert -> {
                    it.condition.detectSuspiciousCondition()
                }
            }
        }
    }

    private fun Expression.detectOperatorWithoutEffect() {
        val root = root()
        when (root.kind) {
            EQUAL_EQUAL -> {
                root.warn("== is comparison, did you mean = instead?")
            }

            IDENTIFIER -> when (type) {
                is FunctionType -> root.warn("missing () for function call")

                else -> root.warn("$root has no effect")
            }

            PLUS, HYPHEN -> when (this) {
                is Binary -> root.warn("$root has no effect, did you mean $root= instead?")

                else -> root.warn("$root has no effect")
            }

            OPENING_PAREN, SIZEOF, OPENING_BRACKET, DOT, HYPHEN_MORE,
            AMPERSAND, ASTERISK, TILDE, BANG, SLASH, PERCENT,
            LESS_LESS, MORE_MORE, LESS, MORE, LESS_EQUAL, MORE_EQUAL, BANG_EQUAL,
            CARET, BAR, AMPERSAND_AMPERSAND, BAR_BAR,
            DOUBLE_CONSTANT, FLOAT_CONSTANT, INTEGER_CONSTANT,
            CHARACTER_CONSTANT, STRING_LITERAL -> {
                root.warn("$root has no effect")
            }

            else -> {
            }
        }
    }

    private fun Expression.detectSuspiciousCondition() {
        when (this) {
            is Assignment -> {
                warn("= is assignment, did you mean == instead?")
            }

            is RelationalEquality -> {
                when {
                    left is RelationalEquality -> {
                        val op1 = left.operator
                        val op2 = this.operator
                        warn("a${op1}b${op2}c does not do what you think it does. You probably want a${op1}b && b${op2}c instead.")
                    }

                    left.type.isString() || right.type.isString() -> {
                        warn("a $operator b compares string addresses. To compare characters: strcmp(a, b) $operator 0")
                    }

                    operator.kind == LESS || operator.kind == BANG_EQUAL -> {
                        if (right is FunctionCall && right.function is Identifier && right.function.name.text == "strlen") {
                            warn("consider replacing SLOW i${operator.kind}strlen(s) with FAST s[i]")
                        }
                    }
                }
            }

            is Logical -> {
                left.detectSuspiciousCondition()
                right.detectSuspiciousCondition()
            }
        }
        val x = value
        if (x is ArithmeticValue && this !is Constant) {
            warn("condition is always ${x.isTrue()}")
        }
    }

    private fun detectUnusedVariables() {
        val unusedVariables = HashSet<Token>()
        translationUnit.walk({ node ->
            when (node) {
                is FunctionDefinition -> {
                    node.parameters.forEach { unusedVariables.add(it.name) }
                }

                is Declaration -> {
                    if (node.specifiers.storageClass != TYPEDEF) {
                        for (namedDeclarator in node.namedDeclarators) {
                            unusedVariables.add(namedDeclarator.name)
                            if (namedDeclarator.declarator is Declarator.Initialized) {
                                namedDeclarator.declarator.init.walk({}) {
                                    when (it) {
                                        is Identifier -> {
                                            unusedVariables.remove(it.symbol.name)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }) { node ->
            when (node) {
                is Identifier -> {
                    unusedVariables.remove(node.symbol.name)
                }
            }
        }
        unusedVariables.forEach {
            it.warn("unused variable $it")
        }
    }

    private fun detectMissingReturns() {
        translationUnit.functions.forEach { it.detectMissingReturn() }
    }

    private fun FunctionDefinition.detectMissingReturn() {
        if (returnType() !== VoidType) {
            val exit = controlFlowGraph.values.last()
            if (exit.isReachable && exit.isOpen()) {
                root().warn("function ${name()} does not return a result on all code paths")
            }
        }
    }

    private fun detectUnreachableCode() {
        translationUnit.functions.forEach { it.detectUnreachableCode() }
    }

    private fun FunctionDefinition.detectUnreachableCode() {
        controlFlowGraph.values.asSequence()
            .filterNot(BasicBlock::isReachable)
            .flatMap(BasicBlock::getStatements)
            .filterNot { it.root().start < 0 }
            .firstOrNull()
            ?.apply {
                if (this is ImplicitContinue) {
                    root().warn("loop never repeats")
                } else {
                    root().warn("unreachable code")
                }
            }
    }
}


================================================
FILE: src/main/kotlin/semantic/LinterBase.kt
================================================
package semantic

import common.Diagnostic
import syntax.lexer.Token
import syntax.tree.Expression

abstract class LinterBase {
    private val warnings = ArrayList<Diagnostic>()

    fun getWarnings(): List<Diagnostic> = warnings.sortedBy { it.position }

    protected fun Token.warn(message: String) {
        warnings.add(Diagnostic(start, message))
    }

    protected fun Expression.warn(message: String) {
        root().warn(message)
    }
}


================================================
FILE: src/main/kotlin/semantic/SymbolTable.kt
================================================
package semantic

import semantic.types.FunctionType
import semantic.types.Type
import syntax.lexer.Token
import syntax.tree.Identifier

data class Symbol(val name: Token, val type: Type, val offset: Int) {
    val usages = ArrayList<Identifier>()

    override fun toString(): String = name.text
}

class SymbolTable {
    private val scopes = Array<HashMap<String, Symbol>>(128) { HashMap() }
    private var current = 0
    private val closedScopes = ArrayList<Map<String, Symbol>>()
    private val allSymbols = ArrayList<Symbol>()

    fun atGlobalScope(): Boolean {
        return current == 0
    }

    fun openScope() {
        scopes[++current] = HashMap()
    }

    fun closeScope() {
        assert(!atGlobalScope()) { "Attempt to close the global scope" }
        if (current == 1) {
            closedScopes.clear()
        } else {
            closedScopes.add(scopes[current])
        }
        --current
    }

    fun reopenScope() {
        ++current
    }

    inline fun <T> scoped(action: () -> T): T {
        openScope()
        val result = action()
        closeScope()
        return result
    }

    inline fun <T> rescoped(action: () -> T): T {
        reopenScope()
        val result = action()
        closeScope()
        return result
    }

    fun lookup(name: Token): Symbol? {
        val text = name.text
        for (i in current downTo 0) {
            scopes[i][text]?.let { symbol -> return symbol }
        }
        return null
    }

    fun lookupInClosedScopes(name: Token): Symbol? {
        val text = name.text
        for (i in closedScopes.lastIndex downTo 0) {
            closedScopes[i][text]?.let { symbol -> return symbol }
        }
        return null
    }

    fun declare(name: Token, type: Type, offset: Int): Symbol {
        return declareIn(scopes[current], name, type, offset)
    }

    fun declareOutside(name: Token, type: Type, offset: Int): Symbol {
        return declareIn(scopes[current - 1], name, type, offset)
    }

    private fun declareIn(scope: HashMap<String, Symbol>, name: Token, type: Type, offset: Int): Symbol {
        val text = name.text
        val previous = scope[text]
        if (previous != null) {
            if (previous.type is FunctionType && !previous.type.defined && type is FunctionType && type.defined) {
                if (previous.type == type) {
                    previous.type.defined = true
                    previous.usages.add(Identifier(name).also { it.symbol = previous })
                    return previous
                } else {
                    name.error("function definition signature does not agree with function declaration signature")
                }
            } else {
                name.error("symbol $name already declared in current scope", previous.name)
            }
        } else {
            val symbol = Symbol(name, type, offset)
            scope[text] = symbol
            allSymbols.add(symbol)
            return symbol
        }
    }

    fun currentFunction(): Symbol? {
        return scopes[0].values.maxByOrNull { symbol -> symbol.name.start }
    }

    fun names(): Sequence<String> = sequence {
        for (i in current downTo 0) {
            for ((_, symbol) in scopes[i]) {
                yield(symbol.name.text)
            }
        }
    }

    fun symbolAt(position: Int): Symbol? {
        for (symbol in allSymbols) {
            if (symbol.name.start <= position && position <= symbol.name.end) return symbol
            for (usage in symbol.usages) {
                if (usage.name.start <= position && position <= usage.name.end) return symbol
            }
        }
        return null
    }
}


================================================
FILE: src/main/kotlin/semantic/TypeChecker.kt
================================================
package semantic

import common.Diagnostic
import freditor.Levenshtein
import interpreter.*
import semantic.types.*
import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.lexer.fakeIdentifier
import syntax.lexer.missingIdentifier
import syntax.tree.*
import text.skipDigits

class TypeChecker(translationUnit: TranslationUnit) {
    private val functionTokens: Map<String, Token> =
        translationUnit.functions.map { it.namedDeclarator.name }.associateBy(Token::text)

    private val symbolTable = SymbolTable()
    val stringLiterals = LinkedHashSet<String>()

    private var staticOffset = Int.MIN_VALUE
    private var currentReturnType: Type = Later
    private var currentStackFrameSymbols = ArrayList<Symbol>()

    init {
        declare(fakeIdentifier("pow"), FunctionType(DoubleType, DoubleType, DoubleType))
        declare(fakeIdentifier("time"), FunctionType(UnsignedIntType, SignedIntType))

        val constCharPointer = PointerType(Const(SignedCharType))
        declare(fakeIdentifier("puts"), FunctionType(VoidType, constCharPointer))
        declare(fakeIdentifier("putchar"), FunctionType(VoidType, SignedIntType))
        declare(fakeIdentifier("getchar"), FunctionType(SignedIntType))

        declare(fakeIdentifier("malloc"), FunctionType(VoidPointerType, UnsignedIntType))
        declare(fakeIdentifier("free"), FunctionType(VoidType, VoidPointerType))
        declare(fakeIdentifier("realloc"), FunctionType(VoidPointerType, VoidPointerType, UnsignedIntType))

        declare(fakeIdentifier("memswap"), FunctionType(VoidType, VoidPointerType, VoidPointerType, UnsignedIntType))

        val predicate = FunctionType(SignedIntType, ConstVoidPointerType, ConstVoidPointerType).pointer()
        declare(
            fakeIdentifier("qsort"),
            FunctionType(VoidType, VoidPointerType, UnsignedIntType, UnsignedIntType, predicate)
        )
        declare(
            fakeIdentifier("bsearch"),
            FunctionType(
                VoidPointerType, ConstVoidPointerType, ConstVoidPointerType, UnsignedIntType, UnsignedIntType, predicate
            )
        )

        declare(fakeIdentifier("strlen"), FunctionType(UnsignedIntType, constCharPointer))
        declare(fakeIdentifier("strcmp"), FunctionType(SignedIntType, constCharPointer, constCharPointer))

        translationUnit.externalDeclarations.forEach {
            when (it) {
                is FunctionDefinition -> it.typeCheck()
                is Declaration -> it.typeCheck()
            }
        }
    }

    private var currentDeclarationIsStatic = false

    private fun declare(name: Token, type: Type): Symbol {
        return if (symbolTable.atGlobalScope()) {
            declareStatic(name, type)
        } else {
            declareAutomatic(name, type)
        }
    }

    private fun declareStatic(name: Token, type: Type): Symbol {
        currentDeclarationIsStatic = true
        val symbol = symbolTable.declare(name, type, staticOffset)
        staticOffset += type.count()
        return symbol
    }

    private fun declareAutomatic(name: Token, type: Type): Symbol {
        currentDeclarationIsStatic = false
        val symbol = symbolTable.declare(name, type, currentStackFrameSymbols.nextOffset())
        if (symbol.type.requiresStorage()) {
            currentStackFrameSymbols.add(symbol)
        }
        return symbol
    }

    private fun ArrayList<Symbol>.nextOffset(): Int {
        if (isEmpty()) return 0
        with(last()) {
            return offset + type.count()
        }
    }

    private fun declareOutside(name: Token, type: Type) {
        assert(type is FunctionType)
        symbolTable.declareOutside(name, type, staticOffset)
    }

    fun symbolAt(position: Int): Symbol? {
        return symbolTable.symbolAt(position)
    }

    private fun DeclarationSpecifiers.typeCheckNoStorageClass(): Type {
        typeCheck()
        if (storageClass != VOID) root().error("no storage class allowed in this context")
        return type
    }

    private fun DeclarationSpecifiers.typeCheck(): Type {
        determineType()
        applyQualifiers()
        return type
    }

    private fun DeclarationSpecifiers.determineType() {
        type = typeSpecifiers[typeTokens]!!
        if (type == Later) {
            type = when (typeTokens.first()) {
                ENUM -> list.firstNotNullOf { it.enumType() }

                STRUCT -> list.firstNotNullOf { it.structType() }

                IDENTIFIER -> {
                    val specifier = list.find { it.kind() == IDENTIFIER }
                    val primitive = specifier as DeclarationSpecifier.Primitive
                    val identifier = primitive.token
                    val symbol = symbolTable.lookup(identifier)!!
                    val alias = symbol.type as Typedef
                    alias.aliased
                }

                else -> root().error("no determineType for ${typeTokens.first()}")
            }
        }
    }

    private fun DeclarationSpecifiers.applyQualifiers() {
        if (qualifiers.contains(CONST)) {
            type = type.addConst()
        }
    }

    private fun DeclarationSpecifier.enumType(): Type? {
        return when (this) {
            is DeclarationSpecifier.EnumDef -> {
                if (name.wasProvided()) declare(name, TypedefSignedIntType)
                var counter = 0
                for (enumerator in body) {
                    with(enumerator) {
                        if (init != null) {
                            val type = init.typeCheck()
                            if (type !is ArithmeticType || !type.isIntegral()) {
                                init.root().error("enumeration constant must be an integral number")
                            }
                            val value =
                                init.value ?: init.root().error("enumeration constant must be a compile-time constant")

                            counter = (value as ArithmeticValue).value.toInt()
                        }
                        declare(name, EnumerationConstant(Value.signedInt(counter)))
                        ++counter
                    }
                }
                SignedIntType
            }

            is DeclarationSpecifier.EnumRef -> SignedIntType

            else -> null
        }
    }

    private fun DeclarationSpecifier.structType(): Type? {
        return when (this) {
            is DeclarationSpecifier.StructDef -> {
                val members = ArrayList<Symbol>()
                val structType = StructType(name, members)
                if (name.wasProvided()) declare(name, StructTag(structType))
                var offset = 0
                for (structDeclaration in body) {
                    val specifierType = structDeclaration.specifiers.typeCheckNoStorageClass()
                    for (namedDeclarator in structDeclaration.declarators) {
                        val type = namedDeclarator.typeCheck(specifierType)
                        validateType(namedDeclarator.name, type)
                        members.add(Symbol(namedDeclarator.name, type, offset))
                        offset += type.count()
                    }
                }
                structType.makeComplete()
            }

            is DeclarationSpecifier.StructRef -> {
                val temp = symbolTable.lookup(name) ?: name.error("undefined struct $name")
                (temp.type as StructTag).structType
            }

            else -> null
        }
    }

    private fun NamedDeclarator.typeCheck(specifierType: Type): Type {
        type = declarator.type(specifierType)
        return type
    }

    private fun FunctionParameter.typeCheck(): Type {
        with(namedDeclarator) {
            typeCheck(specifiers.typeCheckNoStorageClass())
            // retain const on function parameters
            type = type.applyQualifiersTo(type.decayed())
            return type
        }
    }

    private fun Declarator.type(from: Type): Type {
        return when (this) {
            is Declarator.Identity -> from

            is Declarator.Pointer -> child.type(from.pointer().let { if (qualifiers.isEmpty()) it else it.addConst() })

            is Declarator.Array -> child.type(ArrayType(determineSize(), from))

            is Declarator.Function -> child.type(
                // ignore top-level const in function types
                FunctionType(from.unqualified(), parameters.map { it.typeCheck().unqualified() })
            )

            is Declarator.Initialized -> declarator.type(from)
        }
    }

    private fun Declarator.Array.determineSize(): Int {
        if (size == null) return 0

        val type = size.typeCheck()
        if (type !is ArithmeticType || !type.isIntegral()) size.root().error("array size must be an integral number")

        val value = size.value ?: size.root().error("array size must be a compile-time constant")

        val size = (value as ArithmeticValue).value.toInt()
        if (size < 1) this.size.root().error("non-positive array size $size")

        return size
    }

    private fun FunctionDefinition.typeCheck() {
        val functionType = namedDeclarator.typeCheck(specifiers.typeCheckNoStorageClass()) as FunctionType
        functionType.defined = true
        currentReturnType = functionType.returnType
        if (currentReturnType is StructType) {
            // What is the lifetime of a returned struct?
            namedDeclarator.name.error("cannot return structs yet")
        }
        currentStackFrameSymbols = ArrayList()
        symbolTable.scoped {
            for (parameter in parameters) {
                with(parameter) {
                    if (!name.wasProvided()) name.error("missing parameter name in function definition")
                    validateType(name, type)
                    val symbol = declare(name, type)
                    offset = symbol.offset
                }
            }
            declareOutside(namedDeclarator.name, functionType)
            body.typeCheck()
        }
        if (currentStackFrameSymbols.isEmpty()) {
            currentStackFrameSymbols.add(Symbol(missingIdentifier, SignedCharType, 0))
        }
        stackFrameType = StructType(namedDeclarator.name, currentStackFrameSymbols)
    }

    private fun List<Statement>.typeCheck() {
        return forEach { it.typeCheck() }
    }

    private fun Statement.typeCheck() {
        when (this) {
            is Declaration -> {
                typeCheck()
            }

            is Block -> {
                symbolTable.scoped { statements.typeCheck() }
            }

            is ExpressionStatement -> {
                expression.typeCheck()
            }

            is IfThenElse -> {
                condition.typeCheck()
                th3n.typeCheck()
                e1se?.typeCheck()
            }

            is Switch -> {
                control.typeCheck().unqualified().let {
                    if (it !is ArithmeticType || !it.isIntegral()) {
                        switch.error("switch control expression must be of integral type")
                    }
                }
                body.typeCheck()
            }

            is Case -> {
                choice.typeCheck().let {
                    if (it !is ArithmeticType || !it.isIntegral()) {
                        case.error("case label must be an integral constant")
                    }
                    choice.value ?: case.error("case label must be a compile-time constant")
                }
                body.typeCheck()
            }

            is Default -> {
                body.typeCheck()
            }

            is While -> {
                condition.typeCheck()
                body.typeCheck()
            }

            is Do -> {
                body.typeCheck()
                condition.typeCheck()
            }

            is For -> {
                symbolTable.scoped {
                    init?.typeCheck()
                    condition?.typeCheck()
                    update?.typeCheck()
                    body.typeCheck()
                }
            }

            is LabeledStatement -> {
                statement.typeCheck()
            }

            is Goto -> {
            }

            is Continue -> {
            }

            is Break -> {
            }

            is Return -> {
                if (result == null) {
                    if (currentReturnType !== VoidType) r3turn.error("missing return value")
                } else {
                    checkAssignmentCompatibility(currentReturnType, result.root(), result.typeCheck())
                }
            }

            is Assert -> {
                if (condition is Assignment) condition.root().error("= is assignment, did you mean == instead?")
                condition.typeCheck()
            }

            else -> error("no typeCheck for $this")
        }
    }

    private fun Declaration.typeCheck() {
        val specifierType = specifiers.typeCheck()
        for (namedDeclarator in namedDeclarators) {
            val name = namedDeclarator.name
            val type = namedDeclarator.typeCheck(specifierType)
            when (specifiers.storageClass) {
                TYPEDEF -> declare(name, Typedef(type))
                STATIC -> declareStatic(name, type)
                else -> declare(name, type)
            }
            namedDeclarator.offset = symbolTable.lookup(namedDeclarator.name)!!.offset
            val declarator = namedDeclarator.declarator
            if (declarator !is Declarator.Initialized) {
                if (type is ArrayType && type.size == 0) {
                    name.error("missing array size or initializer")
                }
                if (type !is FunctionType) validateType(name, type)
            } else {
                val init = declarator.init
                if (type is ArrayType && type.size == 0) {
                    if (init is InitializerList) {
                        type.size = init.list.size
                    } else if (init is ExpressionInitializer && init.expression is StringLiteral) {
                        type.size = init.expression.literal.text.length + 1
                        init.expression.type = type
                    }
                    if (currentDeclarationIsStatic) staticOffset += type.count()
                }
                typeCheck(type, init)
            }
        }
    }

    private fun typeCheck(qualified: Type, init: Initializer) {
        val type = qualified.unqualified()
        when (init) {
            is ExpressionInitializer -> {
                if (init.expression is StringLiteral && type is ArrayType && type.elementType == SignedCharType) {
                    if (type.size <= init.expression.literal.text.length) {
                        init.expression.root().error("string literal too long")
                    }
                } else {
                    init.expression.typeCheck()
                    if (currentDeclarationIsStatic && init.expression.value == null && init.expression !is StringLiteral) {
                        init.expression.root().error("static initializers must be compile-time constants")
                    }
                    checkAssignmentCompatibility(type, init.expression.root(), init.expression.type)
                }
            }

            is InitializerList -> when (type) {
                is ArrayType -> {
                    val size = type.size
                    if (size < init.list.size) init.list[size].root().error("too many initializers for $type")
                    if (size > init.list.size) init.list.last().root().error("not enough initializers for $type")
                    init.list.forEach { typeCheck(type.elementType, it) }
                }

                is StructType -> {
                    val size = type.members.size
                    if (size < init.list.size) init.list[size].root().error("too many initializers for $type")
                    if (size > init.list.size) init.list.last().root().error("not enough initializers for $type")
                    for ((member, initializer) in type.members.zip(init.list)) {
                        typeCheck(member.type, initializer)
                    }
                }

                else -> init.openBrace.error("cannot initialize $type with braces")
            }
        }
    }

    private var sizeofNesting = 0

    private inline fun sizeofContext(f: () -> Unit) {
        ++sizeofNesting
        try {
            f()
        } finally {
            --sizeofNesting
        }
    }

    private inline fun <T : Unary> T.determineValue(f: (Value) -> Value) {
        if (sizeofNesting == 0) {
            val v = operand.value
            if (v != null) {
                value = f(v)
            }
        }
    }

    private inline fun <T : Binary> T.determineValue(f: (Value, Value) -> Value) {
        if (sizeofNesting == 0) {
            val v = left.value
            if (v != null) {
                val w = right.value
                if (w != null) {
                    value = f(v, w)
                }
            }
        }
    }

    private fun Token.integer(): ArithmeticValue {
        var radix = 10
        var start = 0
        if (text[0] == '0' && text.length >= 2) {
            when (text[1]) {
                'x', 'X' -> {
                    radix = 16
                    start = 2
                }

                'b', 'B' -> {
                    radix = 2
                    start = 2
                }

                else -> {
                    radix = 8
                    start = 1
                }
            }
        }
        try {
            val x = text.substring(start).toLong(radix)
            if (x <= 0x7fffffff) return Value.signedInt(x.toInt())
            if (x <= 0xffffffff) return Value.unsignedInt(x.toInt())
        } catch (_: NumberFormatException) {
        }
        error("integer literal $text is too large, allowed maximum is 4294967295")
    }

    private fun Expression.typeCheck(): Type {
        type = when (this) {
            is Constant -> {
                val constant = constant
                val temp = when (constant.kind) {
                    DOUBLE_CONSTANT -> Value.double(constant.text.toDouble())

                    FLOAT_CONSTANT -> Value.float(constant.text.toFloat())

                    INTEGER_CONSTANT -> constant.integer()

                    CHARACTER_CONSTANT -> Value.signedInt(constant.text[0].code.toByte().toInt())

                    else -> error("no value for $this")
                }
                value = temp
                temp.type
            }

            is StringLiteral -> {
                isLocator = true
                if (sizeofNesting == 0) {
                    stringLiterals.add(literal.text)
                }
                ArrayType(literal.text.length + 1, SignedCharType)
            }

            is Identifier -> {
                val symbol = symbolTable.lookup(name)
                if (symbol == null) {
                    val symbol = symbolTable.lookupInClosedScopes(name)
                    if (symbol != null) {
                        name.error("symbol $name no longer in scope", symbol.name)
                    }
                    val functionToken = functionTokens[name.text]
                    if (functionToken != null && functionToken.start > name.start) {
                        name.error("must declare function before use", functionToken)
                    }
                    val bestMatches = Levenshtein.bestMatches(name.text, symbolTable.names().asIterable())
                    if (bestMatches.size == 1) {
                        val bestMatch = bestMatches.first()
                        val prefix = bestMatch.commonPrefixWith(name.text)
                        name.error("undeclared symbol $name, did you mean $bestMatch?", prefix.length)
                    } else {
                        val commaSeparated = bestMatches.joinToString(", ")
                        name.error("undeclared symbol $name, best matches: $commaSeparated")
                    }
                }

                this.symbol = symbol
                symbol.usages.add(this)

                if (symbol.type is EnumerationConstant) {
                    value = symbol.type.value
                    SignedIntType
                } else {
                    isLocator = (symbol.type !is FunctionType)
                    symbol.type
                }
            }

            is PrintfCall -> {
                checkPrintfFormatString()
                SignedIntType
            }

            is ScanfCall -> {
                arguments.forEach {
                    val type = it.typeCheck().decayed()
                    if (type !is PointerType) it.root().error("missing &")
                    if (type.referencedType is ArrayType && type.referencedType.elementType === SignedCharType) {
                        it.root().error("redundant &")
                    }
                }
                SignedIntType
            }

            is Postfix -> {
                val operandType = operand.typeCheck()
                if (operandType.isConst()) operator.error("const", "$operator")
                if (!operand.isLocator) operator.error("value", "$operator")
                if ((operandType is ArithmeticType) || (operandType is PointerType)) {
                    operandType
                } else {
                    operator.error("$operandType", "$operator")
                }
            }

            is Subscript -> {
                isLocator = true
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is PointerType) && (rightType is ArithmeticType)) {
                    leftType.referencedType
                } else if ((leftType is ArithmeticType) && (rightType is PointerType)) {
                    rightType.referencedType
                } else {
                    operator.error("$leftType", "[$rightType]")
                }
            }

            is FunctionCall -> {
                val functionPointerType = function.typeCheck().decayed()
                if (functionPointerType !is PointerType) function.root().error("$functionPointerType is not a function")
                val functionType = functionPointerType.referencedType
                if (functionType !is FunctionType) function.root().error("$functionType is not a function")

                val parameterTypes = functionType.parameters
                val nParameters = parameterTypes.size
                val nArguments = arguments.size
                if (nParameters != nArguments) function.root()
                    .error("function takes $nParameters arguments, not $nArguments")
                for ((parameterType, argument) in parameterTypes.zip(arguments)) {
                    checkAssignmentCompatibility(parameterType, argument.root(), argument.typeCheck())
                }
                functionType.returnType
            }

            is DirectMemberAccess -> {
                isLocator = true
                val leftType = left.typeCheck()
                val structType = leftType.unqualified()

                if (structType is StructType) {
                    val member = structType.member(right) ?: right.error("$right is not a member of $structType")
                    leftType.applyQualifiersTo(member.type)
                } else if (leftType is PointerType && leftType.referencedType.unqualified() is StructType) {
                    dot.error("replace . with -> for indirect member access")
                } else {
                    left.root().error("$structType is not a struct")
                }
            }

            is IndirectMemberAccess -> {
                isLocator = true
                val leftPointerType = left.typeCheck().decayed()
                if (leftPointerType is StructType) arrow.error("replace -> with . for direct member access")
                if (leftPointerType !is PointerType) left.root().error("$leftPointerType is not a struct pointer")
                val leftType = leftPointerType.referencedType
                val structType = leftType.unqualified()

                if (structType is StructType) {
                    val member = structType.member(right) ?: right.error("$right is not a member of $structType")
                    leftType.applyQualifiersTo(member.type)
                } else {
                    left.root().error("$structType is not a struct")
                }
            }

            is Prefix -> {
                val operandType = operand.typeCheck()
                if (operandType.isConst()) operator.error("${operator}const")
                if (!operand.isLocator) operator.error("${operator}value")
                if ((operandType is ArithmeticType) || (operandType is PointerType)) {
                    operandType
                } else {
                    operator.error("${operator}$operandType")
                }
            }

            is Reference -> {
                val operandType = operand.typeCheck()
                if (operandType !is FunctionType) {
                    if (!operand.isLocator) operator.error("${operator}value")
                } else if (operand is Identifier) {
                    value = FunctionDesignator(operand.name, operandType).decayed()
                }
                PointerType(operandType)
            }

            is Dereference -> {
                val operandType = operand.typeCheck().decayed()
                if (operandType !is PointerType) operator.error("${operator}$operandType")
                isLocator = (operandType.referencedType !is FunctionType)
                operandType.referencedType
            }

            is UnaryPlus -> {
                val operandType = operand.typeCheck().decayed()
                if (operandType !is ArithmeticType) operator.error("${operator}$operandType")
                this.determineValue(::unaryPlus)
                SignedIntType.max(operandType)
            }

            is UnaryMinus -> {
                val operandType = operand.typeCheck().decayed()
                if (operandType !is ArithmeticType) operator.error("${operator}$operandType")
                this.determineValue(::unaryMinus)
                SignedIntType.max(operandType)
            }

            is BitwiseNot -> {
                val operandType = operand.typeCheck().decayed()
                if (operandType !is ArithmeticType || !operandType.isIntegral()) operator.error("${operator}$operandType")
                this.determineValue(::bitwiseNot)
                SignedIntType.max(operandType)
            }

            is LogicalNot -> {
                val operandType = operand.typeCheck().decayed()
                if (operandType !is ArithmeticType) operator.error("${operator}$operandType")
                this.determineValue(::logicalNot)
                SignedIntType
            }

            is SizeofType -> {
                operandType = declarator.type(specifiers.typeCheckNoStorageClass())
                val size = operandType.sizeof()
                if (size == 0) operator.error("sizeof requires object type")
                value = Value.unsignedInt(size)
                UnsignedIntType
            }

            is SizeofExpression -> {
                sizeofContext { operand.typeCheck() }
                val size = operand.type.sizeof()
                if (size == 0) operator.error("sizeof requires object type")
                value = Value.unsignedInt(size)
                UnsignedIntType
            }

            is Multiplicative -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
                    this.determineValue { a, b -> multiplicative(a, operator, b) }
                    leftType.usualArithmeticConversions(rightType)
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Plus -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ComparablePointerType) && (rightType is ArithmeticType)) {
                    leftType
                } else if ((leftType is ArithmeticType) && (rightType is ComparablePointerType)) {
                    rightType
                } else if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
                    this.determineValue(::plus)
                    leftType.usualArithmeticConversions(rightType)
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Minus -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ComparablePointerType) && (rightType is ArithmeticType)) {
                    leftType
                } else if ((leftType is ComparablePointerType) && (rightType is ComparablePointerType)) {
                    SignedIntType
                } else if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
                    this.determineValue(::minus)
                    leftType.usualArithmeticConversions(rightType)
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Shift -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ArithmeticType && leftType.isIntegral()) && (rightType is ArithmeticType && rightType.isIntegral())) {
                    val typ = leftType.integralPromotions()
                    this.determineValue { a, b -> shift(a, operator, b, typ) }
                    typ
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is RelationalEquality -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ComparablePointerType) && (rightType is ComparablePointerType)) {
                    SignedIntType
                } else if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
                    this.determineValue { a, b ->
                        relationalEquality(a as ArithmeticValue, operator, b as ArithmeticValue)
                    }
                    SignedIntType
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Bitwise -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ArithmeticType && leftType.isIntegral()) && (rightType is ArithmeticType && rightType.isIntegral())) {
                    val typ = leftType.usualArithmeticConversions(rightType)
                    this.determineValue { a, b -> bitwise(a, operator, b, typ) }
                    typ
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Logical -> {
                val leftType = left.typeCheck().decayed()
                val rightType = right.typeCheck().decayed()
                if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
                    this.determineValue { a, b ->
                        when (operator.kind) {
                            AMPERSAND_AMPERSAND -> {
                                if ((a as ArithmeticValue).isFalse()) Value.ZERO
                                else (b as ArithmeticValue).normalizeBool()
                            }

                            BAR_BAR -> {
                                if ((a as ArithmeticValue).isTrue()) Value.ONE
                                else (b as ArithmeticValue).normalizeBool()
                            }

                            else -> error("no evaluate for $operator")
                        }
                    }
                    SignedIntType
                } else {
                    operator.error("$leftType ", "$operator $rightType")
                }
            }

            is Conditional -> {
                condition.typeCheck().decayed()
                val a = th3n.typeCheck().decayed()
                val b = e1se.typeCheck().decayed()
                if (a is ArithmeticType && b is ArithmeticType) {
                    a.usualArithmeticConversions(b)
                } else if (a is VoidType && b is VoidType) {
                    VoidType
                } else if (a is PointerType && b is PointerType) {
                    if (a.referencedType == b.referencedType) {
                        a
                    } else {
                        colon.error("$a ", ": $b")
                    }
                } else if (a is ComparablePointerType && b is ComparablePointerType) {
                    // one or more void pointers
                    VoidPointerType
                } else {
                    colon.error("$a ", ": $b")
                }
            }

            is Cast -> {
                val targetType = declarator.type(specifiers.typeCheckNoStorageClass()).unqualified()
                val sourceType = operand.typeCheck()
                checkAssignmentCompatibility(targetType, operator, sourceType)
                this.determineValue { targetType.cast(it) }
                targetType
            }

            is Assignment -> {
                val leftType = left.typeCheck()
                if (leftType.isConst()) operator.error("const ", "$operator ")
                if (!left.isLocator) operator.error("value ", "$operator ")
                val rightType = right.typeCheck()
                checkAssignmentCompatibility(leftType, operator, rightType)
                leftType
            }

            is PlusAssignment -> {
                typeCheckPlusMinusAssignment()
            }

            is MinusAssignment -> {
                typeCheckPlusMinusAssignment()
            }

            is Comma -> {
                left.typeCheck()
                right.typeCheck().decayed()
            }

            else -> error("no typeCheck for $this")
        }
        return type
    }

    private fun Binary.typeCheckPlusMinusAssignment(): Type {
        val leftType = left.typeCheck()
        if (leftType.isConst()) operator.error("const ", "$operator ")
        if (!left.isLocator) operator.error("value ", "$operator ")
        val rightType = right.typeCheck().decayed()
        if ((leftType is ArithmeticType) && (rightType is ArithmeticType)) {
            return leftType
        } else if ((leftType is ComparablePointerType) && (rightType is ArithmeticType)) {
            return leftType
        } else {
            operator.error("$leftType ", "$operator $rightType")
        }
    }

    private fun PrintfCall.checkPrintfFormatString() {
        try {
            val args = arguments.iterator()
            val fmt = format.text
            var k = fmt.indexOf('%')
            while (k != -1) {
                if (fmt[++k] != '%') {
                    k = fmt.skipDigits(k) // width
                    if (fmt[k] == '.') {
                        val dot = k
                        k = fmt.skipDigits(k + 1) // precision
                        if (fmt[k] !in "eEfgGs") format.stringErrorAt(dot, ". only works inside %e %E %f %g %G %s")
                    }
                    if (fmt[k] !in "ciudoxXeEfgGspn") format.stringErrorAt(k, "illegal conversion specifier")
                    if (!args.hasNext()) format.stringErrorAt(k, "missing argument after format string")
                    val arg = args.next()
                    checkPrintfConversionSpecifier(fmt[k], arg.typeCheck().decayed(), arg.root())
                }
                k = fmt.indexOf('%', k + 1)
            }
            if (args.hasNext()) args.next().root().error("missing conversion specifier in format string")
        } catch (ex: StringIndexOutOfBoundsException) {
            throw Diagnostic(format.end - 1, "incomplete conversion specifier")
        }
    }

    private fun checkPrintfConversionSpecifier(specifier: Char, type: Type, where: Token) {
        when (specifier) {
            'c', 'i', 'u', 'd', 'o', 'x', 'X' -> if (type !is ArithmeticType || !type.isIntegral()) {
                where.error("%$specifier expects integral type, not $type")
            }

            'e', 'E', 'f', 'g', 'G' -> if (type !is ArithmeticType || type.isIntegral()) {
                where.error("%$specifier expects floating type, not $type")
            }

            's' -> if (type !is PointerType || type.referencedType.unqualified() !== SignedCharType) {
                where.error("%$specifier expects string, not $type")
            }

            'p' -> if (type !is ComparablePointerType) {
                where.error("%$specifier expects pointer, not $type")
            }

            'n' -> where.error("%$specifier not implemented yet")

            else -> where.error("illegal conversion specifier %$specifier")
        }
    }

    private fun checkAssignmentCompatibility(left: Type, operator: Token, right: Type) {
        if (!left.canCastFrom(right)) {
            operator.error("$right\n cannot be converted to \n$left")
        }
    }

    private fun validateType(name: Token, type: Type) {
        if (!type.isComplete()) name.error("incomplete type $type")
        when (type) {
            is ArrayType -> validateType(name, type.elementType)

            is PointerType -> {
                val t = type.referencedType
                if (t !is StructType && t !is FunctionType) validateType(name, t)
            }
        }
    }
}


================================================
FILE: src/main/kotlin/semantic/TypeSpecifiers.kt
================================================
package semantic

import semantic.types.*
import syntax.lexer.TokenKind.*
import syntax.lexer.TokenKindSet

val enumStructUnion = TokenKindSet.of(ENUM, STRUCT, UNION)
val storageClasses = TokenKindSet.of(TYPEDEF, EXTERN, STATIC, AUTO, REGISTER)

val typeSpecifierIdentifier = TokenKindSet.of(IDENTIFIER)

val typeSpecifiers = mapOf(
    TokenKindSet.of(VOID) to VoidType,
    TokenKindSet.of(CHAR) to SignedCharType,
    TokenKindSet.of(SIGNED, CHAR) to SignedCharType,
    TokenKindSet.of(UNSIGNED, CHAR) to UnsignedCharType,
    TokenKindSet.of(SHORT) to SignedShortType,
    TokenKindSet.of(SHORT, INT) to SignedShortType,
    TokenKindSet.of(SIGNED, SHORT) to SignedShortType,
    TokenKindSet.of(SIGNED, SHORT, INT) to SignedShortType,
    TokenKindSet.of(UNSIGNED, SHORT) to UnsignedShortType,
    TokenKindSet.of(UNSIGNED, SHORT, INT) to UnsignedShortType,
    TokenKindSet.of(INT) to SignedIntType,
    TokenKindSet.of(SIGNED) to SignedIntType,
    TokenKindSet.of(SIGNED, INT) to SignedIntType,
    TokenKindSet.of(UNSIGNED) to UnsignedIntType,
    TokenKindSet.of(UNSIGNED, INT) to UnsignedIntType,
    TokenKindSet.of(LONG) to SignedIntType,
    TokenKindSet.of(LONG, INT) to SignedIntType,
    TokenKindSet.of(SIGNED, LONG) to SignedIntType,
    TokenKindSet.of(SIGNED, LONG, INT) to SignedIntType,
    TokenKindSet.of(UNSIGNED, LONG) to UnsignedIntType,
    TokenKindSet.of(UNSIGNED, LONG, INT) to UnsignedIntType,
    TokenKindSet.of(FLOAT) to FloatType,
    TokenKindSet.of(DOUBLE) to DoubleType,
    TokenKindSet.of(ENUM) to Later,
    TokenKindSet.of(STRUCT) to Later,
    typeSpecifierIdentifier to Later,
)

fun main() {
    var spread = 0x1_0000_0001
    while (!perfect(spread)) {
        spread += 2
    }
    println("return (bits * 0x%x).ushr(32).toInt()".format(spread))
}

private fun perfect(spread: Long): Boolean {
    var set = 0
    for (key in typeSpecifiers.keys) {
        val hash = (key.bits * spread).ushr(32).toInt()
        val index = hash xor (hash ushr 16) and 31
        val mask = 1 shl index
        if (set and mask != 0) return false
        set = set or mask
    }
    return true
}


================================================
FILE: src/main/kotlin/semantic/types/Arithmetic.kt
================================================
package semantic.types

import interpreter.ArithmeticValue
import interpreter.Value
import text.quote

abstract class ArithmeticType : Type {
    abstract fun show(value: Double): String

    abstract val defaultValue: ArithmeticValue

    abstract fun rank(): Int

    open fun isIntegral(): Boolean = true

    abstract fun trim(x: Double): Double

    override fun canCastFromDecayed(source: Type): Boolean = source is ArithmeticType

    override fun cast(source: Value): Value = cast(source as ArithmeticValue)

    fun cast(source: ArithmeticValue): ArithmeticValue =
        if (source.type === this) source else ArithmeticValue(trim(source.value), this)

    fun integralPromotions(): ArithmeticType = this.max(SignedIntType)

    fun usualArithmeticConversions(that: ArithmeticType): ArithmeticType = integralPromotions().max(that)

    fun max(that: ArithmeticType): ArithmeticType = if (this.rank() < that.rank()) that else this

    override fun declaration(parent: String): String = "$this$parent"
}

object SignedCharType : ArithmeticType() {
    override fun sizeof(): Int = 1

    override fun show(value: Double): String = value.toInt().and(0xff).toChar().quote()

    override val defaultValue: ArithmeticValue = Value.NUL

    override fun rank(): Int = 0

    override fun trim(x: Double): Double {
        if (x < -128.0) throw ArithmeticException("char underflow $x")
        if (x > +127.0) throw ArithmeticException("char overflow $x")
        return x.toInt().toByte().toDouble()
    }

    override fun toString(): String = "char"
}

object UnsignedCharType : ArithmeticType() {
    override fun sizeof(): Int = 1

    override fun show(value: Double): String = value.toInt().toString()

    override val defaultValue: ArithmeticValue = Value.unsignedChar(0)

    override fun rank(): Int = 1

    override fun trim(x: Double): Double = x.toInt().and(0xff).toDouble()

    override fun toString(): String = "unsigned char"
}

object SignedShortType : ArithmeticType() {
    override fun sizeof(): Int = 2

    override fun show(value: Double): String = value.toInt().toString()

    override val defaultValue: ArithmeticValue = Value.signedShort(0)

    override fun rank(): Int = 2

    override fun trim(x: Double): Double {
        if (x < -32768.0) throw ArithmeticException("short underflow $x")
        if (x > +32767.0) throw ArithmeticException("short overflow $x")
        return x.toInt().toShort().toDouble()
    }

    override fun toString(): String = "short"
}

object UnsignedShortType : ArithmeticType() {
    override fun sizeof(): Int = 2

    override fun show(value: Double): String = value.toInt().toString()

    override val defaultValue: ArithmeticValue = Value.unsignedShort(0)

    override fun rank(): Int = 3

    override fun trim(x: Double): Double = x.toInt().and(0xffff).toDouble()

    override fun toString(): String = "unsigned short"
}

object SignedIntType : ArithmeticType() {
    override fun sizeof(): Int = 4

    override fun show(value: Double): String = value.toInt().toString()

    override val defaultValue: ArithmeticValue by lazy { Value.signedInt(0) }

    override fun rank(): Int = 4

    override fun trim(x: Double): Double {
        if (x < -2147483648.0) throw ArithmeticException("int underflow $x")
        if (x > +2147483647.0) throw ArithmeticException("int overflow $x")
        return x.toInt().toDouble()
    }

    override fun toString(): String = "int"
}

object UnsignedIntType : ArithmeticType() {
    override fun sizeof(): Int = 4

    override fun show(value: Double): String = value.toLong().toString()

    override val defaultValue: ArithmeticValue by lazy { Value.unsignedInt(0) }

    override fun rank(): Int = 5

    override fun trim(x: Double): Double = x.toLong().and(0xffffffff).toDouble()

    override fun toString(): String = "unsigned int"
}

object FloatType : ArithmeticType() {
    override fun sizeof(): Int = 4

    override fun show(value: Double): String = value.toFloat().toString()

    override val defaultValue: ArithmeticValue = Value.float(0f)

    override fun rank(): Int = 8

    override fun isIntegral(): Boolean = false

    override fun trim(x: Double): Double = x.toFloat().toDouble()

    override fun toString(): String = "float"
}

object DoubleType : ArithmeticType() {
    override fun sizeof(): Int = 8

    override fun show(value: Double): String = value.toString()

    override val defaultValue: ArithmeticValue = Value.double(0.0)

    override fun rank(): Int = 9

    override fun isIntegral(): Boolean = false

    override fun trim(x: Double): Double = x

    override fun toString(): String = "double"
}


================================================
FILE: src/main/kotlin/semantic/types/Array.kt
================================================
package semantic.types

data class ArrayType(var size: Int, val elementType: Type) : Type {
    override fun sizeof(): Int = size * elementType.sizeof()

    override fun sizeof(offset: Int): Int {
        if (offset >= count()) return sizeof()

        val n = elementType.count()
        return offset / n * elementType.sizeof() + elementType.sizeof(offset % n)
    }

    override fun decayed(): Type = PointerType(elementType)

    override fun count(): Int = size * elementType.count()

    override fun isConst(): Boolean = elementType.isConst()

    override fun addConst(): Type = if (isConst()) this else ArrayType(size, elementType.addConst())

    override fun unqualified(): Type = if (isConst()) ArrayType(size, elementType.unqualified()) else this

    fun dimensions(): Int = if (elementType is ArrayType) elementType.dimensions() + 1 else 1

    override fun toString(): String = declaration("")

    override fun declaration(parent: String): String {
        return if (parent.isPointer()) {
            elementType.declaration("($parent)[$size]")
        } else {
            elementType.declaration("$parent[$size]")
        }
    }
}

fun Type.isString(): Boolean = this is ArrayType && elementType.unqualified() == SignedCharType


================================================
FILE: src/main/kotlin/semantic/types/Enum.kt
================================================
package semantic.types

import interpreter.ArithmeticValue

class EnumerationConstant(val value: ArithmeticValue) : Type {
    override fun requiresStorage(): Boolean = false

    override fun count(): Int = 0
}


================================================
FILE: src/main/kotlin/semantic/types/Function.kt
================================================
package semantic.types

data class FunctionType(val returnType: Type, val parameters: List<Type>) : Type {
    companion object {
        operator fun invoke(returnType: Type, vararg parameters: Type) = FunctionType(returnType, parameters.toList())

        fun declarationMarker(): FunctionType = FunctionType(VoidType)
        val DEFINITION_MARKER: FunctionType = declarationMarker().apply { defined = true }
    }

    var defined: Boolean = false

    override fun requiresStorage(): Boolean = false

    override fun count(): Int = 0

    override fun decayed(): Type = pointer()

    override fun toString(): String = declaration("")

    override fun declaration(parent: String): String {
        val params = parameters.joinToString(transform = Type::toString, prefix = "(", separator = ",", postfix = ")")
        return if (parent.isPointer()) {
            returnType.declaration("($parent)$params")
        } else {
            returnType.declaration("$parent$params")
        }
    }
}


================================================
FILE: src/main/kotlin/semantic/types/Pointer.kt
================================================
package semantic.types

import interpreter.Value

interface ComparablePointerType : Type

data class PointerType(val referencedType: Type) : ComparablePointerType {
    override fun sizeof(): Int = 4

    override fun canCastFromDecayed(source: Type): Boolean {
        if (source === VoidPointerType) return true

        if (source === ConstVoidPointerType) return referencedType.isConst()

        return canCastFromPointer(source)
    }

    private fun canCastFromPointer(source: Type): Boolean {
        if (source !is PointerType) return false

        val sourceReferenced = source.referencedType
        return referencedType == sourceReferenced || referencedType.unqualified() == sourceReferenced
    }

    override fun cast(source: Value): Value {
        if (!canCastFromPointer(source.type().decayed()))
            throw AssertionError("${source.type()}\n cannot be converted to \n$this")

        return source.decayed()
    }

    override fun toString(): String = declaration("")

    override fun declaration(parent: String): String {
        return referencedType.declaration("*$parent")
    }
}

object VoidPointerType : ComparablePointerType {
    override fun sizeof(): Int = 4

    override fun canCastFromDecayed(source: Type): Boolean {
        if (source === this) return true
        return source is PointerType && !source.referencedType.isConst()
    }

    override fun toString(): String = "void*"

    override fun declaration(parent: String): String = "void*$parent"
}

object ConstVoidPointerType : ComparablePointerType {
    override fun sizeof(): Int = 4

    override fun canCastFromDecayed(source: Type): Boolean {
        if (source === this || source === VoidPointerType) return true
        return source is PointerType
    }

    override fun toString(): String = "const void*"

    override fun declaration(parent: String): String = "const void*$parent"
}


================================================
FILE: src/main/kotlin/semantic/types/Struct.kt
================================================
package semantic.types

import semantic.Symbol
import syntax.lexer.Token
import syntax.lexer.missingIdentifier

abstract class CompletableType : Type {
    private var complete = false

    override fun isComplete(): Boolean = complete

    fun makeComplete(): Type {
        assert(!complete)
        complete = true
        return this
    }
}

class StructType(val name: Token, val members: List<Symbol>) : CompletableType() {
    override fun sizeof(): Int = members.sumOf { it.type.sizeof() }

    override fun sizeof(offset: Int): Int {
        var size = 0
        var off = offset
        for (member in members) {
            size += member.type.sizeof(off)
            off -= member.type.count()
            if (off <= 0) break
        }
        return size
    }

    override fun count(): Int = members.sumOf { it.type.count() }

    override fun canCastFromDecayed(source: Type): Boolean = (this === source)

    fun member(name: Token): Symbol? = members.find { it.name.text === name.text }

    override fun toString(): String = "struct $name"

    override fun declaration(parent: String): String = "struct $name$parent"
}

val StructTypeLater = StructType(missingIdentifier, emptyList())

class StructTag(val structType: StructType) : Type {
    override fun requiresStorage(): Boolean = false

    override fun count(): Int = 0
}


================================================
FILE: src/main/kotlin/semantic/types/Type.kt
================================================
package semantic.types

import interpreter.Value

interface Type {
    fun requiresStorage(): Boolean = true

    fun isComplete(): Boolean = sizeof() > 0

    fun sizeof(): Int = 0

    fun sizeof(offset: Int): Int = if (offset == 0) 0 else sizeof()

    fun decayed(): Type = this

    fun pointer(): Type = PointerType(this)

    fun count(): Int = 1

    fun canCastFrom(source: Type): Boolean = canCastFromDecayed(source.decayed())

    fun canCastFromDecayed(source: Type): Boolean = false

    fun cast(source: Value): Value = source

    fun isConst(): Boolean = false

    fun addConst(): Type = Const(this)

    fun unqualified(): Type = this

    fun applyQualifiersTo(target: Type): Type = target

    fun declaration(parent: String): String {
        throw AssertionError("$javaClass.declaration($parent)")
    }

    fun String.isPointer(): Boolean = this.isNotEmpty() && this.first() == '*'
}

data class Const(val underlying: Type) : Type by underlying {
    override fun pointer(): Type = PointerType(this)

    override fun isConst(): Boolean = true

    override fun addConst(): Type = this

    override fun unqualified(): Type = underlying

    override fun applyQualifiersTo(target: Type): Type = target.addConst()

    override fun toString(): String = declaration("")

    override fun declaration(parent: String): String {
        return if (underlying is ComparablePointerType) {
            underlying.declaration("const$parent")
        } else {
            "const $underlying$parent"
        }
    }
}

object Later : Type


================================================
FILE: src/main/kotlin/semantic/types/Typedef.kt
================================================
package semantic.types

class Typedef(val aliased: Type) : Type {
    override fun requiresStorage(): Boolean = false

    override fun count(): Int = 0
}

val TypedefSignedIntType = Typedef(SignedIntType)

object MarkerIsTypedefName : Type

object MarkerNotTypedefName : Type


================================================
FILE: src/main/kotlin/semantic/types/Void.kt
================================================
package semantic.types

object VoidType : Type {
    override fun pointer(): Type = VoidPointerType

    override fun count(): Int = 0

    override fun addConst(): Type = ConstVoidType

    override fun toString(): String = "void"

    override fun declaration(parent: String): String = "void$parent"
}

object ConstVoidType : Type {
    override fun pointer(): Type = ConstVoidPointerType

    override fun count(): Int = 0

    override fun isConst(): Boolean = true

    override fun addConst(): Type = this

    override fun unqualified(): Type = VoidType

    override fun toString(): String = "const void"

    override fun declaration(parent: String): String = "const void$parent"
}


================================================
FILE: src/main/kotlin/syntax/lexer/Characters.kt
================================================
package syntax.lexer

import syntax.lexer.TokenKind.CHARACTER_CONSTANT
import syntax.lexer.TokenKind.STRING_LITERAL

fun Lexer.characterConstant(): Token {
    val executionChar = when (next()) {
        '\\' -> escapeSequence()

        in '\u0020'..'\u007e' -> current
        in '\u00a0'..'\u00ff' -> current

        else -> error("illegal character inside character constant")
    }
    if (next() != '\'') error("character constant must be closed by '")

    next()
    return token(CHARACTER_CONSTANT, lexeme(), executionChar.toString())
}

fun Lexer.escapeSequence(): Char = when (next()) {
    '\'', '\"', '?', '\\' -> current

    'a' -> '\u0007'
    'b' -> '\u0008'
    't' -> '\u0009'
    'n' -> '\u000a'
    'v' -> '\u000b'
    'f' -> '\u000c'
    'r' -> '\u000d'

    '0' -> '\u0000'

    else -> error("illegal escape character")
}

fun Lexer.stringLiteral(): Token {
    val sb = StringBuilder()
    while (true) {
        val executionChar = when (next()) {
            '\\' -> escapeSequence()

            '\"' -> {
                next()
                return token(STRING_LITERAL, lexeme(), sb.toString().intern())
            }

            in '\u0020'..'\u007e' -> current
            in '\u00a0'..'\u00ff' -> current

            else -> error("illegal character inside string literal")
        }
        sb.append(executionChar)
    }
}


================================================
FILE: src/main/kotlin/syntax/lexer/Identifiers.kt
================================================
package syntax.lexer

import syntax.lexer.TokenKind.IDENTIFIER

tailrec fun Lexer.identifierOrKeyword(): Token = when (next()) {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '_', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> identifierOrKeyword()

    else -> {
        val lexeme = lexeme()
        when (val value: Any? = identifiersOrKeywords[lexeme]) {
            is TokenKind -> verbatim(value)

            is String -> token(IDENTIFIER, value)

            else -> {
                identifiersOrKeywords[lexeme] = lexeme
                token(IDENTIFIER, lexeme)
            }
        }
    }
}


================================================
FILE: src/main/kotlin/syntax/lexer/Lexer.kt
================================================
package syntax.lexer

import common.Diagnostic

const val EOF = '\u0000'

class Lexer(private val input: String) {
    var start: Int = -1
        private set

    var index: Int = -1
        private set

    fun startAtIndex() {
        start = index
    }

    var current: Char = next()
        private set

    fun next(): Char {
        current = if (++index < input.length) input[index] else EOF
        return current
    }

    fun previous(): Char {
        current = input[--index]
        return current
    }

    fun lexeme(): String {
        return input.substring(start, index)
    }

    fun token(kind: TokenKind): Token {
        return token(kind, lexeme())
    }

    fun token(kind: TokenKind, text: String): Token {
        return token(kind, text, text)
    }

    fun token(kind: TokenKind, source: String, text: String): Token {
        return Token(kind, start, source, text)
    }

    fun verbatim(kind: TokenKind): Token {
        return token(kind, kind.lexeme)
    }

    fun nextVerbatim(kind: TokenKind): Token {
        next()
        return verbatim(kind)
    }

    fun error(message: String): Nothing {
        throw Diagnostic(index, message)
    }

    val identifiersOrKeywords = HashMap<String, Any>(keywords)
}


================================================
FILE: src/main/kotlin/syntax/lexer/NextToken.kt
================================================
package syntax.lexer

import syntax.lexer.TokenKind.*

tailrec fun Lexer.nextToken(): Token {
    startAtIndex()
    return when (current) {
        ' ', '\u0009', '\u000a', '\u000b', '\u000c', '\u000d' -> {
            next()
            nextToken()
        }

        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        '_' -> identifierOrKeyword()

        '1', '2', '3', '4', '5', '6', '7', '8', '9' -> constant()
        '0' -> zero()

        '\'' -> characterConstant()

        '\"' -> stringLiteral()

        '(' -> nextVerbatim(OPENING_PAREN)
        ')' -> nextVerbatim(CLOSING_PAREN)
        ',' -> nextVerbatim(COMMA)
        '.' -> nextVerbatim(DOT)
        ':' -> nextVerbatim(COLON)
        ';' -> nextVerbatim(SEMICOLON)
        '?' -> nextVerbatim(QUESTION)
        '[' -> nextVerbatim(OPENING_BRACKET)
        ']' -> nextVerbatim(CLOSING_BRACKET)
        '{' -> nextVerbatim(OPENING_BRACE)
        '}' -> nextVerbatim(CLOSING_BRACE)
        '~' -> nextVerbatim(TILDE)

        '!' -> when (next()) {
            '=' -> nextVerbatim(BANG_EQUAL)
            else -> verbatim(BANG)
        }

        '%' -> when (next()) {
            '=' -> nextVerbatim(PERCENT_EQUAL)
            else -> verbatim(PERCENT)
        }

        '&' -> when (next()) {
            '=' -> nextVerbatim(AMPERSAND_EQUAL)
            '&' -> nextVerbatim(AMPERSAND_AMPERSAND)
            else -> verbatim(AMPERSAND)
        }

        '*' -> when (next()) {
            '=' -> nextVerbatim(ASTERISK_EQUAL)
            else -> verbatim(ASTERISK)
        }

        '+' -> when (next()) {
            '=' -> nextVerbatim(PLUS_EQUAL)
            '+' -> nextVerbatim(PLUS_PLUS)
            else -> verbatim(PLUS)
        }

        '-' -> when (next()) {
            '=' -> nextVerbatim(HYPHEN_EQUAL)
            '-' -> nextVerbatim(HYPHEN_HYPHEN)
            '>' -> nextVerbatim(HYPHEN_MORE)
            else -> verbatim(HYPHEN)
        }

        '/' -> when (next()) {
            '/' -> {
                skipSingleLineComment()
                nextToken()
            }

            '*' -> {
                skipMultiLineComment()
                nextToken()
            }

            '=' -> nextVerbatim(SLASH_EQUAL)

            else -> verbatim(SLASH)
        }

        '<' -> when (next()) {
            '=' -> nextVerbatim(LESS_EQUAL)

            '<' -> when (next()) {
                '=' -> nextVerbatim(LESS_LESS_EQUAL)
                else -> verbatim(LESS_LESS)
            }

            else -> verbatim(LESS)
        }

        '=' -> when (next()) {
            '=' -> nextVerbatim(EQUAL_EQUAL)
            else -> verbatim(EQUAL)
        }

        '>' -> when (next()) {
            '=' -> nextVerbatim(MORE_EQUAL)

            '>' -> when (next()) {
                '=' -> nextVerbatim(MORE_MORE_EQUAL)
                else -> verbatim(MORE_MORE)
            }

            else -> verbatim(MORE)
        }

        '^' -> when (next()) {
            '=' -> nextVerbatim(CARET_EQUAL)
            else -> verbatim(CARET)
        }

        '|' -> when (next()) {
            '=' -> nextVerbatim(BAR_EQUAL)
            '|' -> nextVerbatim(BAR_BAR)
            else -> verbatim(BAR)
        }

        EOF -> verbatim(END_OF_INPUT)

        else -> error("illegal input character")
    }
}


================================================
FILE: src/main/kotlin/syntax/lexer/Numbers.kt
================================================
package syntax.lexer

import syntax.lexer.TokenKind.*

fun Lexer.constant(): Token {
    var seenDecimalPoint = false
    while (true) {
        when (next()) {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
            }

            'f', 'F' -> {
                next()
                return token(FLOAT_CONSTANT)
            }

            '.' -> {
                if (seenDecimalPoint) return token(DOUBLE_CONSTANT)
                seenDecimalPoint = true
            }

            else -> return token(if (seenDecimalPoint) DOUBLE_CONSTANT else INTEGER_CONSTANT)
        }
    }
}

fun Lexer.zero(): Token {
    next()
    if (current == 'x' || current == 'X') return hexadecimal()
    if (current == 'b' || current == 'B') return binary()
    previous()

    var seen8or9 = false
    var seenDecimalPoint = false
    while (true) {
        when (next()) {
            '0', '1', '2', '3', '4', '5', '6', '7' -> {
            }

            '8', '9' -> {
                seen8or9 = true
            }

            'f', 'F' -> {
                next()
                return token(FLOAT_CONSTANT)
            }

            '.' -> {
                if (seenDecimalPoint) return token(DOUBLE_CONSTANT)
                seenDecimalPoint = true
            }

            else -> {
                if (seenDecimalPoint) return token(DOUBLE_CONSTANT)
                if (!seen8or9) return token(INTEGER_CONSTANT)

                error("octal literal indicated by leading digit 0 cannot contain digit 8 or 9")
            }
        }
    }
}

fun Lexer.hexadecimal(): Token {
    while (true) {
        when (next()) {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F',
            'a', 'b', 'c', 'd', 'e', 'f' -> {
            }

            else -> {
                if (index - start > 2) return token(INTEGER_CONSTANT)

                error("hexadecimal literal indicated by leading ${lexeme()} must contain at least one digit")
            }
        }
    }
}

fun Lexer.binary(): Token {
    while (true) {
        when (next()) {
            '0', '1' -> {
            }

            else -> {
                if (index - start > 2) return token(INTEGER_CONSTANT)

                error("binary literal indicated by leading ${lexeme()} must contain at least one digit")
            }
        }
    }
}


================================================
FILE: src/main/kotlin/syntax/lexer/SkipComments.kt
================================================
package syntax.lexer

fun Lexer.skipSingleLineComment() {
    while (next() != '\n') {
        if (current == EOF) return
    }
    next() // skip '\n'
}

fun Lexer.skipMultiLineComment() {
    next() // skip '*'
    do {
        if (current == EOF) return
    } while ((current != '*') or (next() != '/'))
    next() // skip '/'
}


================================================
FILE: src/main/kotlin/syntax/lexer/Token.kt
================================================
package syntax.lexer

import common.Diagnostic
import syntax.lexer.TokenKind.IDENTIFIER
import syntax.lexer.TokenKind.STRING_LITERAL

class Token(val kind: TokenKind, val start: Int, val source: String, val text: String) {
    val end: Int
        get() = start + source.length

    fun withTokenKind(replacement: TokenKind): Token = Token(replacement, start, source, text)

    fun tagged(): Token = Token(kind, start, source, "#$text".intern())

    fun wasProvided(): Boolean = kind == IDENTIFIER

    fun error(message: String): Nothing {
        throw Diagnostic(start, message)
    }

    fun error(message: String, columnDelta: Int): Nothing {
        throw Diagnostic(start, message, columnDelta = columnDelta)
    }

    fun error(before: String, after: String): Nothing {
        throw Diagnostic(start, before + after, columnDelta = -before.length)
    }

    fun error(message: String, previous: Token): Nothing {
        throw Diagnostic(start, message, previous.start)
    }

    //  0123 456  index
    // "ABC\nXYZ"
    // 0123456789 origin
    fun stringErrorAt(index: Int, message: String): Nothing {
        assert(kind == STRING_LITERAL)
        var origin = 1
        repeat(index) {
            if (source[origin] == '\\') ++origin
            ++origin
        }
        throw Diagnostic(start + origin, message)
    }

    override fun toString(): String = source
}

fun fakeIdentifier(name: String) = Token(IDENTIFIER, Int.MIN_VALUE, name, name)

val missingIdentifier = fakeIdentifier("")
val hiddenIdentifier = fakeIdentifier("_")


================================================
FILE: src/main/kotlin/syntax/lexer/TokenKind.kt
================================================
package syntax.lexer

enum class TokenKind(val lexeme: String) {
    ASSERT("assert"),
    AUTO("auto"),
    BREAK("break"),
    CASE("case"),
    CHAR("char"),
    CONST("const"),
    CONTINUE("continue"),
    DEFAULT("default"),
    DO("do"),
    DOUBLE("double"),
    ELSE("else"),
    ENUM("enum"),
    EXTERN("extern"),
    FLOAT("float"),
    FOR("for"),
    GOTO("goto"),
    IF("if"),
    INT("int"),
    LONG("long"),
    REGISTER("register"),
    RETURN("return"),
    SHORT("short"),
    SIGNED("signed"),
    SIZEOF("sizeof"),
    STATIC("static"),
    STRUCT("struct"),
    SWITCH("switch"),
    TYPEDEF("typedef"),
    UNION("union"),
    UNSIGNED("unsigned"),
    VOID("void"),
    VOLATILE("volatile"),
    WHILE("while"),

    FLOAT_CONSTANT("FLOAT CONSTANT"),
    DOUBLE_CONSTANT("DOUBLE CONSTANT"),
    INTEGER_CONSTANT("INTEGER CONSTANT"),
    CHARACTER_CONSTANT("CHARACTER CONSTANT"),
    STRING_LITERAL("STRING LITERAL"),
    IDENTIFIER("IDENTIFIER"),
    PRINTF("PRINTF"),
    SCANF("SCANF"),
    END_OF_INPUT("END OF INPUT"),

    OPENING_BRACKET("["),
    CLOSING_BRACKET("]"),
    OPENING_PAREN("("),
    CLOSING_PAREN(")"),
    DOT("."),
    HYPHEN_MORE("->"),
    PLUS_PLUS("++"),
    HYPHEN_HYPHEN("--"),
    AMPERSAND("&"),
    ASTERISK("*"),
    PLUS("+"),
    HYPHEN("-"),
    TILDE("~"),
    BANG("!"),
    SLASH("/"),
    PERCENT("%"),
    LESS_LESS("<<"),
    MORE_MORE(">>"),
    LESS("<"),
    MORE(">"),
    LESS_EQUAL("<="),
    MORE_EQUAL(">="),
    EQUAL_EQUAL("=="),
    BANG_EQUAL("!="),
    CARET("^"),
    BAR("|"),
    AMPERSAND_AMPERSAND("&&"),
    BAR_BAR("||"),
    QUESTION("?"),
    COLON(":"),
    EQUAL("="),
    ASTERISK_EQUAL("*="),
    SLASH_EQUAL("/="),
    PERCENT_EQUAL("%="),
    PLUS_EQUAL("+="),
    HYPHEN_EQUAL("-="),
    LESS_LESS_EQUAL("<<="),
    MORE_MORE_EQUAL(">>="),
    AMPERSAND_EQUAL("&="),
    CARET_EQUAL("^="),
    BAR_EQUAL("|="),
    COMMA(","),
    OPENING_BRACE("{"),
    CLOSING_BRACE("}"),
    SEMICOLON(";");

    override fun toString(): String = lexeme

    companion object {
        val KEYWORDS = entries.subList(ASSERT.ordinal, WHILE.ordinal + 1)
        val OPERATORS_SEPARATORS = entries.subList(OPENING_BRACKET.ordinal, SEMICOLON.ordinal + 1)
    }
}

val keywords: Map<String, TokenKind> = TokenKind.KEYWORDS.associateBy(TokenKind::lexeme)


================================================
FILE: src/main/kotlin/syntax/lexer/TokenKindSet.kt
================================================
package syntax.lexer

import java.lang.Long.lowestOneBit
import java.lang.Long.numberOfTrailingZeros

private val TokenKind.bitmask: Long
    get() {
        assert(ordinal < Long.SIZE_BITS) { "$this@$ordinal is not among the first ${Long.SIZE_BITS} enum constants" }
        return 1L shl ordinal
    }

class TokenKindSet(val bits: Long) {
    companion object {
        val EMPTY = TokenKindSet(0L)

        fun of(kind: TokenKind): TokenKindSet {
            return TokenKindSet(kind.bitmask)
        }

        fun of(kind1: TokenKind, kind2: TokenKind): TokenKindSet {
            return TokenKindSet(kind1.bitmask or kind2.bitmask)
        }

        fun of(kind1: TokenKind, kind2: TokenKind, kind3: TokenKind): TokenKindSet {
            return TokenKindSet(kind1.bitmask or kind2.bitmask or kind3.bitmask)
        }

        fun of(kind1: TokenKind, kind2: TokenKind, kind3: TokenKind, kind4: TokenKind): TokenKindSet {
            return TokenKindSet(kind1.bitmask or kind2.bitmask or kind3.bitmask or kind4.bitmask)
        }

        fun of(kind1: TokenKind, kind2: TokenKind, kind3: TokenKind, kind4: TokenKind, kind5: TokenKind): TokenKindSet {
            return TokenKindSet(kind1.bitmask or kind2.bitmask or kind3.bitmask or kind4.bitmask or kind5.bitmask)
        }
    }

    fun contains(kind: TokenKind): Boolean {
        return (bits and kind.bitmask) != 0L
    }

    operator fun plus(kind: TokenKind): TokenKindSet {
        return TokenKindSet(bits or kind.bitmask)
    }

    fun isEmpty(): Boolean {
        return bits == 0L
    }

    fun first(): TokenKind {
        return TokenKind.entries[numberOfTrailingZeros(bits)]
    }

    override fun equals(other: Any?): Boolean {
        return other is TokenKindSet && this.bits == other.bits
    }

    override fun hashCode(): Int {
        return (bits * 0x10418282f).ushr(32).toInt()
    }

    override fun toString(): String {
        return generateSequence(bits) { b -> b xor lowestOneBit(b) }
            .takeWhile { b -> b != 0L }
            .map { b -> TokenKind.entries[numberOfTrailingZeros(b)] }
            .joinToString(", ", "[", "]")
    }
}


================================================
FILE: src/main/kotlin/syntax/parser/Autocompletion.kt
================================================
package syntax.parser

import common.Diagnostic
import syntax.lexer.Lexer
import syntax.lexer.TokenKind

fun autocompleteIdentifier(textBeforeSelection: String): List<String> {
    val lexer = Lexer(textBeforeSelection)
    val parser = Parser(lexer)
    val suffixes = parser.fittingSuffixes(textBeforeSelection)
    val lcp = longestCommonPrefix(suffixes)
    return if (lcp.isEmpty()) {
        suffixes
    } else {
        listOf(lcp)
    }
}

private fun Parser.fittingSuffixes(textBeforeSelection: String): List<String> {
    try {
        translationUnit()
    } catch (diagnostic: Diagnostic) {
        if (diagnostic.position != textBeforeSelection.length) throw diagnostic

        if (previous.kind == TokenKind.IDENTIFIER && previous.end == textBeforeSelection.length) {
            return when (beforePrevious.kind) {
                TokenKind.DOT, TokenKind.HYPHEN_MORE -> suffixesIn(allMemberNames.asSequence())

                else -> suffixesIn(symbolTable.names())
            }
        }
    }
    return emptyList()
}

private fun Parser.suffixesIn(names: Sequence<String>): List<String> {
    val prefix = previous.text
    val prefixLength = prefix.length

    return names
        .filter { it.length > prefixLength && it.startsWith(prefix) }
        .map { it.substring(prefixLength) }
        .toList()
}

private fun longestCommonPrefix(strings: List<String>): String {
    val shortestString = strings.minByOrNull(String::length) ?: ""
    shortestString.forEachIndexed { index, ch ->
        if (!strings.all { it[index] == ch }) {
            return shortestString.substring(0, index)
        }
    }
    return shortestString
}


================================================
FILE: src/main/kotlin/syntax/parser/Declarations.kt
================================================
package syntax.parser

import semantic.enumStructUnion
import semantic.storageClasses
import semantic.typeSpecifierIdentifier
import semantic.typeSpecifiers
import semantic.types.FunctionType
import semantic.types.MarkerIsTypedefName
import semantic.types.MarkerNotTypedefName
import syntax.lexer.Token
import syntax.lexer.TokenKind
import syntax.lexer.TokenKind.*
import syntax.lexer.TokenKindSet
import syntax.tree.*

fun Parser.declare(namedDeclarator: NamedDeclarator, isTypedefName: Boolean) {
    val pseudoType = when {
        isTypedefName -> MarkerIsTypedefName

        namedDeclarator.declarator.leaf() is Declarator.Function -> FunctionType.declarationMarker()

        else -> MarkerNotTypedefName
    }
    symbolTable.declare(namedDeclarator.name, pseudoType, 0)
}

fun Parser.isTypedefName(token: Token): Boolean {
    val symbol = symbolTable.lookup(token)
    if (symbol == null) {
        val taggedSymbol = symbolTable.lookup(token.tagged())
        if (taggedSymbol != null) {
            token.error("Did you forget struct/enum/union before ${token.text}?")
        }
    }
    return symbol?.type === MarkerIsTypedefName
}

fun Parser.isDeclarationSpecifier(token: Token): Boolean = when (token.kind) {
    TYPEDEF, EXTERN, STATIC, AUTO, REGISTER,
    CONST, VOLATILE,
    VOID, CHAR, SHORT, INT, LONG, SIGNED, UNSIGNED, FLOAT, DOUBLE,
    ENUM, STRUCT, UNION -> true

    IDENTIFIER -> isTypedefName(token)

    else -> false
}

fun Parser.declaration(): Statement {
    val specifiers = declarationSpecifiers1declareDefTagName()
    val isTypedef = specifiers.storageClass == TYPEDEF
    val declarators = commaSeparatedList0(SEMICOLON) {
        initDeclarator().apply { declare(this, isTypedef) }
    }
    if (current == OPENING_BRACE) {
        val inner = declarators.last().name
        val outer = symbolTable.currentFunction()!!.name
        inner.error("cannot define function $inner inside function $outer", outer)
    }
    return Declaration(specifiers, declarators).semicolon()
}

fun Parser.declarationSpecifiers1declareDefTagName(): DeclarationSpecifiers {
    val specifiers = declarationSpecifiers1()
    specifiers.defTagName()?.let { name ->
        symbolTable.declare(name, MarkerNotTypedefName, 0)
    }
    return specifiers
}

fun Parser.declarationSpecifiers1(): DeclarationSpecifiers {
    val specifiers = declarationSpecifiers0()
    if (specifiers.list.isEmpty()) {
        val symbol = symbolTable.lookup(token)
        if (symbol != null) {
            val taggedSymbol = symbolTable.lookup(token.tagged())
            if (taggedSymbol != null) {
                token.error("Did you forget struct/enum/union before ${token.text}?")
            }
        }
        illegalStartOf("declaration")
    }
    return specifiers
}

fun Parser.declarationSpecifiers0(): DeclarationSpecifiers {
    var storageClass: TokenKind = VOID
    var qualifiers = TokenKindSet.EMPTY
    var typeTokens = TokenKindSet.EMPTY
    val list = ArrayList<DeclarationSpecifier>()

    loop@ while (true) {
        when (current) {
            TYPEDEF, EXTERN, STATIC, AUTO, REGISTER ->
                if (storageClass == VOID) {
                    storageClass = current
                } else {
                    val previous = list.first { storageClasses.contains(it.kind()) }
                    token.error("multiple storage class specifiers", previous.root())
                }

            CONST, VOLATILE ->
                if (qualifiers.contains(current)) {
                    val previous = list.first { it.kind() == current }
                    token.error("duplicate type qualifier", previous.root())
                } else {
                    qualifiers += current
                }

            IDENTIFIER ->
                if (typeTokens.isEmpty() && isTypedefName(token)) {
                    typeTokens = typeSpecifierIdentifier
                } else {
                    break@loop
                }

            VOID, CHAR, SHORT, INT, LONG, SIGNED, UNSIGNED, FLOAT, DOUBLE,
            ENUM, STRUCT, UNION ->
                if (!typeTokens.isEmpty() && enumStructUnion.contains(typeTokens.first())) {
                    val previous = list.first { enumStructUnion.contains(it.kind()) }
                    token.error(
                        "Did you forget to terminate the previous ${previous.kind()} with a semicolon?",
                        previous.root()
                    )
                } else if (typeTokens.contains(current)) {
                    val previous = list.first { it.kind() == current }
                    token.error("duplicate type specifier", previous.root())
                } else {
                    typeTokens += current
                    if (typeTokens !in typeSpecifiers) token.error("illegal combination of type specifiers: $typeTokens")
                }

            else -> break@loop
        }
        list.add(declarationSpecifier())
    }
    return DeclarationSpecifiers(list, storageClass, qualifiers, typeTokens)
}

fun Parser.declarationSpecifier(): DeclarationSpecifier = when (current) {
    ENUM -> enumSpecifier()

    STRUCT -> structSpecifier()

    UNION -> notImplementedYet("unions")

    else -> DeclarationSpecifier.Primitive(accept())
}

fun Parser.enumSpecifier(): DeclarationSpecifier {
    return if (next() == OPENING_BRACE) {
        // anonymous enum
        DeclarationSpecifier.EnumDef(token, enumBody())
    } else {
        val name = expect(IDENTIFIER).tagged()
        if (current == OPENING_BRACE) {
            // named enum
            DeclarationSpecifier.EnumDef(name, enumBody())
        } else {
            DeclarationSpecifier.EnumRef(name)
        }
    }
}

fun Parser.enumBody(): List<Enumerator> {
    return braced {
        commaSeparatedList1 {
            Enumerator(expect(IDENTIFIER), optional(EQUAL, ::assignmentExpression)).apply {
                symbolTable.declare(name, MarkerNotTypedefName, 0)
            }
        }
    }
}

fun Parser.structSpecifier(): DeclarationSpecifier {
    return if (next() == OPENING_BRACE) {
        // anonymous struct
        DeclarationSpecifier.StructDef(token, structBody())
    } else {
        val name = expect(IDENTIFIER).tagged()
        if (current == OPENING_BRACE) {
            // named struct
            DeclarationSpecifier.StructDef(name, structBody())
        } else {
            DeclarationSpecifier.StructRef(name)
        }
    }
}

fun Parser.structBody(): List<StructDeclaration> {
    return braced {
        list1Until(CLOSING_BRACE) {
            StructDeclaration(declarationSpecifiers1(), commaSeparatedList1 {
                namedDeclarator().apply { allMemberNames.add(name.text) }
            }).semicolon()
        }
    }
}

fun Parser.initDeclarator(): NamedDeclarator {
    return initDeclarator(namedDeclarator())
}

fun Parser.initDeclarator(namedDeclarator: NamedDeclarator): NamedDeclarator {
    return if (current == EQUAL) {
        next()
        with(namedDeclarator) {
            NamedDeclarator(name, Declarator.Initialized(declarator, initializer()))
        }
    } else {
        namedDeclarator
    }
}

fun Parser.initializer(): Initializer {
    return if (current == OPENING_BRACE) {
        InitializerList(token, braced { trailingCommaSeparatedList1(CLOSING_BRACE, ::initializer) })
    } else {
        ExpressionInitializer(assignmentExpression())
    }
}

fun Parser.namedDeclarator(): NamedDeclarator {
    if (current == ASTERISK) {
        next()
        val qualifiers = typeQualifierList()
        return namedDeclarator().map { Declarator.Pointer(it, qualifiers) }
    }
    var temp: NamedDeclarator = when (current) {
        OPENING_PAREN -> parenthesized(::namedDeclarator)

        IDENTIFIER -> NamedDeclarator(accept(), Declarator.Identity)

        else -> illegalStartOf("declarator")
    }
    while (true) {
        temp = when (current) {
            OPENING_BRACKET -> temp.map { Declarator.Array(it, declaratorArray()) }

            OPENING_PAREN -> temp.map { Declarator.Function(it, declaratorFunction()) }

            else -> return temp
        }
    }
}

fun Parser.typeQualifierList(): List<Token> {
    return collectWhile { current == CONST }
}

fun Parser.declaratorArray(): Expression? {
    expect(OPENING_BRACKET)
    return ::expression optionalBefore CLOSING_BRACKET
}

fun Parser.declaratorFunction(): List<FunctionParameter> {
    return symbolTable.scoped {
        parenthesized {
            if (current == VOID && lookahead.kind == CLOSING_PAREN) {
                next()
            }
            commaSeparatedList0(CLOSING_PAREN) {
                val specifiers = declarationSpecifiers1declareDefTagName()
                val declarator = parameterDeclarator()
                if (declarator.name.wasProvided()) {
                    declare(declarator, isTypedefName = false)
                }
                FunctionParameter(specifiers, declarator)
            }
        }
    }
}

fun Parser.abstractDeclarator(): Declarator {
    if (current == ASTERISK) {
        next()
        val qualifiers = typeQualifierList()
        return Declarator.Pointer(abstractDeclarator(), qualifiers)
    }
    var temp: Declarator = when (current) {
        OPENING_PAREN -> parenthesized(::abstractDeclarator)

        IDENTIFIER -> token.error("identifier in abstract declarator")

        else -> Declarator.Identity
    }
    while (true) {
        temp = when (current) {
            OPENING_BRACKET -> Declarator.Array(temp, declaratorArray())

            OPENING_PAREN -> Declarator.Function(temp, declaratorFunction())

            else -> return temp
        }
    }
}

fun Parser.parameterDeclarator(): NamedDeclarator {
    if (current == ASTERISK) {
        next()
        val qualifiers = typeQualifierList()
        return parameterDeclarator().map { Declarator.Pointer(it, qualifiers) }
    }
    var temp: NamedDeclarator = when (current) {
        OPENING_PAREN -> {
            if (isDeclarationSpecifier(lookahead)) {
                NamedDeclarator(token, Declarator.Function(Declarator.Identity, declaratorFunction()))
            } else {
                parenthesized(::parameterDeclarator)
            }
        }

        IDENTIFIER -> NamedDeclarator(accept(), Declarator.Identity)

        else -> NamedDeclarator(token, Declarator.Identity)
    }
    while (true) {
        temp = when (current) {
            OPENING_BRACKET -> temp.map { Declarator.Array(it, declaratorArray()) }

            OPENING_PAREN -> temp.map { Declarator.Function(it, declaratorFunction()) }

            else -> return temp
        }
    }
}


================================================
FILE: src/main/kotlin/syntax/parser/Expressions.kt
================================================
package syntax.parser

import syntax.lexer.TokenKind
import syntax.lexer.TokenKind.*
import syntax.tree.*

const val PRECEDENCE_POSTFIX = 150
const val PRECEDENCE_PREFIX = 140
const val PRECEDENCE_COMMA = 10

fun Parser.condition(): Expression {
    return parenthesized(::expression)
}

@Suppress("NOTHING_TO_INLINE")
inline fun Parser.expression(): Expression {
    return subexpression(outerPrecedence = 0)
}

@Suppress("NOTHING_TO_INLINE")
inline fun Parser.assignmentExpression(): Expression {
    return subexpression(outerPrecedence = PRECEDENCE_COMMA)
}

fun Parser.functionCallArgument(): Expression {
    if (isDeclarationSpecifier(token)) {
        token.error("function call arguments require no types, \nas opposed to function definition parameters")
    }
    return subexpression(outerPrecedence = PRECEDENCE_COMMA)
}

fun Parser.subexpression(outerPrecedence: Int): Expression {
    val nullDenotation = nullDenotations[current.ordinal] ?: illegalStartOf("expression")

    return subexpression(with(nullDenotation) { parse(accept()) }, outerPrecedence)
}

tailrec fun Parser.subexpression(left: Expression, outerPrecedence: Int): Expression {
    val leftDenotation = leftDenotations[current.ordinal] ?: return left
    if (leftDenotation.precedence <= outerPrecedence) return left

    return subexpression(with(leftDenotation) { parse(left, accept()) }, outerPrecedence)
}

private val nullDenotations = arrayOfNulls<NullDenotation>(128).apply {
    this[IDENTIFIER] = IdentifierDenotation
    this[DOUBLE_CONSTANT, FLOAT_CONSTANT, INTEGER_CONSTANT, CHARACTER_CONSTANT] = ConstantDenotation
    this[STRING_LITERAL] = StringLiteralDenotation
    this[OPENING_PAREN] = PossibleCastDenotation
    this[PLUS_PLUS, HYPHEN_HYPHEN, AMPERSAND, ASTERISK, PLUS, HYPHEN, TILDE, BANG] = PrefixDenotation
    this[SIZEOF] = SizeofDenotation
}

private val leftDenotations = arrayOfNulls<LeftDenotation>(128).apply {
    this[OPENING_BRACKET] = SubscriptDenotation
    this[OPENING_PAREN] = FunctionCallDenotation
    this[DOT] = DirectMemberDenotation
    this[HYPHEN_MORE] = IndirectMemberDenotation
    this[PLUS_PLUS, HYPHEN_HYPHEN] = PostfixCrementDenotation

    this[ASTERISK, SLASH, PERCENT] = LeftAssociativeDenotation(130, ::Multiplicative)
    this[PLUS] = LeftAssociativeDenotation(120, ::Plus)
    this[HYPHEN] = LeftAssociativeDenotation(120, ::Minus)
    this[LESS_LESS, MORE_MORE] = LeftAssociativeDenotation(110, ::Shift)
    this[LESS, MORE, LESS_EQUAL, MORE_EQUAL] = LeftAssociativeDenotation(100, ::RelationalEquality)
    this[EQUAL_EQUAL, BANG_EQUAL] = LeftAssociativeDenotation(90, ::RelationalEquality)
    this[AMPERSAND] = LeftAssociativeDenotation(80, ::Bitwise)
    this[CARET] = LeftAssociativeDenotation(70, ::Bitwise)
    this[BAR] = LeftAssociativeDenotation(60, ::Bitwise)

    this[AMPERSAND_AMPERSAND] = RightAssociativeDenotation(50, ::Logical)
    this[BAR_BAR] = RightAssociativeDenotation(40, ::Logical)
    this[QUESTION] = ConditionalDenotation(30)
    this[EQUAL] = RightAssociativeDenotation(20, ::Assignment)
    this[PLUS_EQUAL] = RightAssociativeDenotation(20, ::PlusAssignment)
    this[HYPHEN_EQUAL] = RightAssociativeDenotation(20, ::MinusAssignment)
    this[COMMA] = RightAssociativeDenotation(PRECEDENCE_COMMA, ::Comma)
}

private operator fun <V> Array<V>.set(index: TokenKind, value: V) {
    this[index.ordinal] = value
}

private operator fun <V> Array<V>.set(vararg indexes: TokenKind, value: V) {
    for (index in indexes) {
        this[index.ordinal] = value
    }
}


================================================
FILE: src/main/kotlin/syntax/parser/ExternalDefinitions.kt
================================================
package syntax.parser

import semantic.types.FunctionType
import syntax.lexer.TokenKind.*
import syntax.tree.*

fun Parser.translationUnit(): TranslationUnit {
    return TranslationUnit(list1Until(END_OF_INPUT, ::externalDeclaration))
}

fun Parser.externalDeclaration(): Node {
    val specifiers = declarationSpecifiers1declareDefTagName()
    if (current == SEMICOLON && specifiers.isDeclaratorOptional()) {
        return Declaration(specifiers, emptyList()).semicolon()
    }
    val firstNamedDeclarator = namedDeclarator()
    if (firstNamedDeclarator.declarator.leaf() is Declarator.Function) {
        if (current == SEMICOLON && lookahead.kind == OPENING_BRACE) {
            token.error("function definitions require no semicolon")
        }
        if (current == OPENING_BRACE) {
            symbolTable.declare(firstNamedDeclarator.name, FunctionType.DEFINITION_MARKER, 0)
            return symbolTable.rescoped {
                braced {
                    FunctionDefinition(specifiers, firstNamedDeclarator, list0Until(CLOSING_BRACE, ::statement), token)
                }
            }
        }
    }
    val isTypedefName = specifiers.storageClass == TYPEDEF
    declare(firstNamedDeclarator, isTypedefName)
    val declarators = commaSeparatedList1(initDeclarator(firstNamedDeclarator)) {
        initDeclarator().apply { declare(this, isTypedefName) }
    }
    return Declaration(specifiers, declarators).semicolon()
}


================================================
FILE: src/main/kotlin/syntax/parser/LeftDenotations.kt
================================================
package syntax.parser

import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.tree.*

abstract class LeftDenotation(val precedence: Int) {
    abstract fun Parser.parse(left: Expression, operator: Token): Expression
}

object SubscriptDenotation : LeftDenotation(PRECEDENCE_POSTFIX) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return Subscript(left, operator, expression() before CLOSING_BRACKET)
    }
}

object FunctionCallDenotation : LeftDenotation(PRECEDENCE_POSTFIX) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return FunctionCall(left, commaSeparatedList0(CLOSING_PAREN, ::functionCallArgument) before CLOSING_PAREN)
    }
}

object DirectMemberDenotation : LeftDenotation(PRECEDENCE_POSTFIX) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return DirectMemberAccess(left, operator, expect(IDENTIFIER))
    }
}

object IndirectMemberDenotation : LeftDenotation(PRECEDENCE_POSTFIX) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return IndirectMemberAccess(left, operator, expect(IDENTIFIER))
    }
}

object PostfixCrementDenotation : LeftDenotation(PRECEDENCE_POSTFIX) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return Postfix(left, operator)
    }
}

class LeftAssociativeDenotation(precedence: Int, val factory: (Expression, Token, Expression) -> Expression) :
    LeftDenotation(precedence) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return factory(left, operator, subexpression(precedence))
    }
}

class RightAssociativeDenotation(precedence: Int, val factory: (Expression, Token, Expression) -> Expression) :
    LeftDenotation(precedence) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return factory(left, operator, subexpression(precedence - 1))
    }
}

class ConditionalDenotation(precedence: Int) : LeftDenotation(precedence) {
    override fun Parser.parse(left: Expression, operator: Token): Expression {
        return Conditional(left, operator, expression(), expect(COLON), subexpression(precedence - 1))
    }
}


================================================
FILE: src/main/kotlin/syntax/parser/NullDenotations.kt
================================================
package syntax.parser

import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.tree.*

abstract class NullDenotation {
    abstract fun Parser.parse(token: Token): Expression
}

object IdentifierDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        return when (token.text) {
            "printf" -> parenthesized { printfCall(token.withTokenKind(PRINTF)) }
            "scanf" -> parenthesized { scanfCall(token.withTokenKind(SCANF)) }

            else -> Identifier(token)
        }
    }

    private fun Parser.printfCall(printf: Token): Expression {
        val format = expect(STRING_LITERAL)
        val arguments = if (current == COMMA) {
            next()
            commaSeparatedList1(::assignmentExpression)
        } else {
            emptyList()
        }
        return PrintfCall(printf, format, arguments)
    }

    private fun Parser.scanfCall(scanf: Token): Expression {
        val format = expect(STRING_LITERAL)
        val arguments = if (current == COMMA) {
            next()
            commaSeparatedList1(::assignmentExpression)
        } else {
            emptyList()
        }
        return ScanfCall(scanf, format, arguments)
    }
}

object ConstantDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        return Constant(token)
    }
}

object StringLiteralDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        return StringLiteral(token)
    }
}

object PossibleCastDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        val specifiers = declarationSpecifiers0()
        return if (specifiers.list.isEmpty()) {
            expression() before CLOSING_PAREN
        } else {
            val declarator = abstractDeclarator() before CLOSING_PAREN
            Cast(token, specifiers, declarator, subexpression(PRECEDENCE_PREFIX))
        }
    }
}

object PrefixDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        val operand = subexpression(PRECEDENCE_PREFIX)
        return when (token.kind) {
            PLUS_PLUS, HYPHEN_HYPHEN -> Prefix(token, operand)
            AMPERSAND -> Reference(token, operand)
            ASTERISK -> Dereference(token, operand)
            PLUS -> UnaryPlus(token, operand)
            HYPHEN -> UnaryMinus(token, operand)
            TILDE -> BitwiseNot(token, operand)
            BANG -> LogicalNot(token, operand)

            else -> error("no parse for $token")
        }
    }
}

object SizeofDenotation : NullDenotation() {
    override fun Parser.parse(token: Token): Expression {
        return if (current == OPENING_PAREN && isDeclarationSpecifier(lookahead)) {
            parenthesized { SizeofType(token, declarationSpecifiers0(), abstractDeclarator()) }
        } else {
            SizeofExpression(token, subexpression(PRECEDENCE_PREFIX))
        }
    }
}


================================================
FILE: src/main/kotlin/syntax/parser/Parser.kt
================================================
package syntax.parser

import common.Diagnostic
import semantic.SymbolTable
import syntax.lexer.Lexer
import syntax.lexer.Token
import syntax.lexer.TokenKind
import syntax.lexer.TokenKind.*
import syntax.lexer.nextToken

class Parser(private val lexer: Lexer) {
    var beforePrevious: Token = Token(END_OF_INPUT, 0, "", "")
        private set

    var previous: Token = Token(END_OF_INPUT, 0, "", "")
        private set

    var token: Token = lexer.nextToken()
        private set

    var current: TokenKind = token.kind
        private set

    var lookahead: Token = lexer.nextToken()
        private set

    fun next(): TokenKind {
        beforePrevious = previous
        previous = token
        token = lookahead
        current = token.kind
        lookahead = lexer.nextToken()
        return current
    }

    fun accept(): Token {
        val result = token
        next()
        return result
    }

    fun expect(expected: TokenKind): Token {
        if (current != expected) throw Diagnostic(previous.end, "expected $expected")
        return accept()
    }

    fun <T> T.semicolon(): T {
        expect(SEMICOLON)
        return this
    }

    infix fun <T> T.before(expected: TokenKind): T {
        expect(expected)
        return this
    }

    fun illegalStartOf(rule: String): Nothing {
        token.error("illegal start of $rule")
    }

    fun notImplementedYet(feature: String): Nothing {
        token.error("$feature not implemented yet")
    }

    inline fun <T> commaSeparatedList1(first: T, parse: () -> T): List<T> {
        val list = mutableListOf(first)
        while (current == COMMA) {
            next()
            list.add(parse())
        }
        return list
    }

    inline fun <T> commaSeparatedList1(parse: () -> T): List<T> {
        return commaSeparatedList1(parse(), parse)
    }

    inline fun <T> commaSeparatedList0(terminator: TokenKind, parse: () -> T): List<T> {
        return if (current == terminator) {
            emptyList()
        } else {
            commaSeparatedList1(parse)
        }
    }

    inline fun <T> trailingCommaSeparatedList1(terminator: TokenKind, parse: () -> T): List<T> {
        val list = mutableListOf(parse())
        while (current == COMMA && next() != terminator) {
            list.add(parse())
        }
        return list
    }

    inline fun <T> list1While(proceed: () -> Boolean, parse: () -> T): List<T> {
        val list = mutableListOf(parse())
        while (proceed()) {
            list.add(parse())
        }
        return list
    }

    inline fun <T> list0While(proceed: () -> Boolean, parse: () -> T): List<T> {
        return if (!proceed()) {
            emptyList()
        } else {
            list1While(proceed, parse)
        }
    }

    inline fun <T> list1Until(terminator: TokenKind, parse: () -> T): List<T> {
        return list1While({ current != terminator }, parse)
    }

    inline fun <T> list0Until(terminator: TokenKind, parse: () -> T): List<T> {
        return list0While({ current != terminator }, parse)
    }

    inline fun collectWhile(proceed: () -> Boolean): List<Token> {
        return list0While(proceed, ::accept)
    }

    inline fun <T> parenthesized(parse: () -> T): T {
        expect(OPENING_PAREN)
        val result = parse()
        expect(CLOSING_PAREN)
        return result
    }

    inline fun <T> braced(parse: () -> T): T {
        expect(OPENING_BRACE)
        val result = parse()
        expect(CLOSING_BRACE)
        return result
    }

    infix fun <T> (() -> T).optionalBefore(terminator: TokenKind): T? {
        return if (current == terminator) {
            next()
            null
        } else {
            this() before terminator
        }
    }

    inline fun <T> optional(indicator: TokenKind, parse: () -> T): T? {
        return if (current != indicator) {
            null
        } else {
            next()
            parse()
        }
    }

    val symbolTable = SymbolTable()
    val allMemberNames = HashSet<String>()
}


================================================
FILE: src/main/kotlin/syntax/parser/Statements.kt
================================================
package syntax.parser

import common.Diagnostic
import syntax.lexer.Token
import syntax.lexer.TokenKind.*
import syntax.tree.*

fun Parser.statement(): Statement = when (current) {

    IF -> IfThenElse(accept(), condition(), statement(), optional(ELSE, ::statement))

    SWITCH -> Switch(accept(), condition(), statement())

    CASE -> Case(accept(), expression() before COLON, statement())

    DEFAULT -> Default(accept() before COLON, statement())

    WHILE -> While(accept(), condition(), statement())

    DO -> Do(accept(), statement() before WHILE, condition()).semicolon()

    FOR -> symbolTable.scoped {
        val f0r = accept() before OPENING_PAREN
        For(
            f0r,
            forInit(f0r),
            ::expression optionalBefore SEMICOLON,
            ::expression optionalBefore CLOSING_PAREN,
            statement()
        )
    }

    GOTO -> Goto(accept(), expect(IDENTIFIER)).semicolon()

    CONTINUE -> Continue(accept()).semicolon()

    BREAK -> Break(accept()).semicolon()

    RETURN -> Return(accept(), ::expression optionalBefore SEMICOLON)

    ASSERT -> Assert(accept(), expression()).semicolon()

    TYPEDEF, EXTERN, STATIC, AUTO, REGISTER, CONST, VOLATILE,
    VOID, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED,
    STRUCT, UNION, ENUM -> declaration()

    OPENING_BRACE -> symbolTable.scoped {
        Block(token, braced {
            list0Until(CLOSING_BRACE, ::statement)
        })
    }

    IDENTIFIER -> when {
        lookahead.kind == COLON -> LabeledStatement(accept() before COLON, statement())

        isTypedefName(token) -> declaration()

        else -> ExpressionStatement(expression()).semicolon()
    }

    SEMICOLON -> token.error("unexpected semicolon")

    else -> ExpressionStatement(expression()).semicolon()
}

private fun Parser.forInit(f0r: Token): Statement? = when (current) {
    SEMICOLON -> null.semicolon()

    TYPEDEF, EXTERN, STATIC, AUTO, REGISTER, CONST, VOLATILE,
    VOID, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED,
    STRUCT, UNION, ENUM -> forInitDeclaration(f0r)

    IDENTIFIER -> when {
        isTypedefName(token) -> forInitDeclaration
Download .txt
gitextract_y9tqg1bi/

├── .gitignore
├── README.md
├── pom.xml
├── sloc
└── src/
    ├── main/
    │   └── kotlin/
    │       ├── Main.kt
    │       ├── common/
    │       │   ├── Counter.kt
    │       │   ├── Diagnostic.kt
    │       │   └── Maps.kt
    │       ├── interpreter/
    │       │   ├── BasicBlock.kt
    │       │   ├── BuildControlFlowGraph.kt
    │       │   ├── Console.kt
    │       │   ├── FlatStatements.kt
    │       │   ├── Interpreter.kt
    │       │   ├── Memory.kt
    │       │   ├── Segment.kt
    │       │   └── Value.kt
    │       ├── semantic/
    │       │   ├── Linter.kt
    │       │   ├── LinterBase.kt
    │       │   ├── SymbolTable.kt
    │       │   ├── TypeChecker.kt
    │       │   ├── TypeSpecifiers.kt
    │       │   └── types/
    │       │       ├── Arithmetic.kt
    │       │       ├── Array.kt
    │       │       ├── Enum.kt
    │       │       ├── Function.kt
    │       │       ├── Pointer.kt
    │       │       ├── Struct.kt
    │       │       ├── Type.kt
    │       │       ├── Typedef.kt
    │       │       └── Void.kt
    │       ├── syntax/
    │       │   ├── lexer/
    │       │   │   ├── Characters.kt
    │       │   │   ├── Identifiers.kt
    │       │   │   ├── Lexer.kt
    │       │   │   ├── NextToken.kt
    │       │   │   ├── Numbers.kt
    │       │   │   ├── SkipComments.kt
    │       │   │   ├── Token.kt
    │       │   │   ├── TokenKind.kt
    │       │   │   └── TokenKindSet.kt
    │       │   ├── parser/
    │       │   │   ├── Autocompletion.kt
    │       │   │   ├── Declarations.kt
    │       │   │   ├── Expressions.kt
    │       │   │   ├── ExternalDefinitions.kt
    │       │   │   ├── LeftDenotations.kt
    │       │   │   ├── NullDenotations.kt
    │       │   │   ├── Parser.kt
    │       │   │   └── Statements.kt
    │       │   └── tree/
    │       │       ├── DeclarationSpecifier.kt
    │       │       ├── Declarator.kt
    │       │       ├── Expression.kt
    │       │       ├── External.kt
    │       │       ├── Node.kt
    │       │       └── Statement.kt
    │       ├── text/
    │       │   ├── Char.kt
    │       │   └── String.kt
    │       └── ui/
    │           ├── Flexer.kt
    │           ├── MainFrame.kt
    │           └── MemoryUI.kt
    └── test/
        ├── kotlin/
        │   ├── interpreter/
        │   │   └── InterpreterTest.kt
        │   ├── semantic/
        │   │   └── types/
        │   │       └── TypeToStringTest.kt
        │   └── syntax/
        │       ├── lexer/
        │       │   └── LexerTest.kt
        │       └── parser/
        │           └── AutocompletionTest.kt
        └── resources/
            └── junit-platform.properties
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (318K chars).
[
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": "/target/\n/.idea/\n/*.iml\n"
  },
  {
    "path": "README.md",
    "chars": 6885,
    "preview": "![swap](skorbut.png)\n\n## What is Skorbut?\n\nSkorbut is a simple teaching environment for a subset of C with a memory visu"
  },
  {
    "path": "pom.xml",
    "chars": 5011,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "sloc",
    "chars": 486,
    "preview": "# sloc: count significant lines of code\n# INsignificant lines contain only spaces and/or braces\n\n# $1 directory\n# $2 ext"
  },
  {
    "path": "src/main/kotlin/Main.kt",
    "chars": 201,
    "preview": "import freditor.SwingConfig\nimport ui.MainFrame\nimport java.awt.EventQueue\n\nfun main() {\n    SwingConfig.metalWithDefaul"
  },
  {
    "path": "src/main/kotlin/common/Counter.kt",
    "chars": 215,
    "preview": "package common\n\nclass Counter {\n    private val counter = HashMap<Any, Int>()\n\n    fun count(x: Any): Int {\n        val "
  },
  {
    "path": "src/main/kotlin/common/Diagnostic.kt",
    "chars": 367,
    "preview": "package common\n\ndata class Diagnostic(\n    val position: Int, override val message: String,\n    val secondPosition: Int "
  },
  {
    "path": "src/main/kotlin/common/Maps.kt",
    "chars": 697,
    "preview": "package common\n\nfun <K, V, M : MutableMap<K, V>> M.puts(keys: Array<out K>, value: V): M {\n    for (key in keys) {\n     "
  },
  {
    "path": "src/main/kotlin/interpreter/BasicBlock.kt",
    "chars": 1014,
    "preview": "package interpreter\n\nclass BasicBlock {\n    private val statements = ArrayList<FlatStatement>()\n    fun getStatements():"
  },
  {
    "path": "src/main/kotlin/interpreter/BuildControlFlowGraph.kt",
    "chars": 8971,
    "preview": "package interpreter\n\nimport semantic.types.ArithmeticType\nimport syntax.lexer.missingIdentifier\nimport syntax.tree.*\n\nva"
  },
  {
    "path": "src/main/kotlin/interpreter/Console.kt",
    "chars": 10083,
    "preview": "package interpreter\n\nimport semantic.types.DoubleType\nimport semantic.types.FloatType\nimport semantic.types.SignedCharTy"
  },
  {
    "path": "src/main/kotlin/interpreter/FlatStatements.kt",
    "chars": 2069,
    "preview": "package interpreter\n\nimport syntax.lexer.Token\nimport syntax.lexer.missingIdentifier\nimport syntax.tree.DeclarationSpeci"
  },
  {
    "path": "src/main/kotlin/interpreter/Interpreter.kt",
    "chars": 35809,
    "preview": "package interpreter\n\nimport common.Diagnostic\nimport semantic.TypeChecker\nimport semantic.types.*\nimport syntax.lexer.Le"
  },
  {
    "path": "src/main/kotlin/interpreter/Memory.kt",
    "chars": 4087,
    "preview": "package interpreter\n\nimport semantic.Symbol\nimport semantic.types.ArrayType\nimport semantic.types.SignedCharType\nimport "
  },
  {
    "path": "src/main/kotlin/interpreter/Segment.kt",
    "chars": 1788,
    "preview": "package interpreter\n\nimport semantic.types.Type\n\nclass Segment(val type: Type) {\n    private val memory: MutableList<Val"
  },
  {
    "path": "src/main/kotlin/interpreter/Value.kt",
    "chars": 6656,
    "preview": "package interpreter\n\nimport semantic.types.*\nimport syntax.lexer.Token\n\ndata class Object(val segment: Segment, val offs"
  },
  {
    "path": "src/main/kotlin/semantic/Linter.kt",
    "chars": 6858,
    "preview": "package semantic\n\nimport interpreter.ArithmeticValue\nimport interpreter.BasicBlock\nimport interpreter.ImplicitContinue\ni"
  },
  {
    "path": "src/main/kotlin/semantic/LinterBase.kt",
    "chars": 451,
    "preview": "package semantic\n\nimport common.Diagnostic\nimport syntax.lexer.Token\nimport syntax.tree.Expression\n\nabstract class Linte"
  },
  {
    "path": "src/main/kotlin/semantic/SymbolTable.kt",
    "chars": 3680,
    "preview": "package semantic\n\nimport semantic.types.FunctionType\nimport semantic.types.Type\nimport syntax.lexer.Token\nimport syntax."
  },
  {
    "path": "src/main/kotlin/semantic/TypeChecker.kt",
    "chars": 38382,
    "preview": "package semantic\n\nimport common.Diagnostic\nimport freditor.Levenshtein\nimport interpreter.*\nimport semantic.types.*\nimpo"
  },
  {
    "path": "src/main/kotlin/semantic/TypeSpecifiers.kt",
    "chars": 2131,
    "preview": "package semantic\n\nimport semantic.types.*\nimport syntax.lexer.TokenKind.*\nimport syntax.lexer.TokenKindSet\n\nval enumStru"
  },
  {
    "path": "src/main/kotlin/semantic/types/Arithmetic.kt",
    "chars": 4654,
    "preview": "package semantic.types\n\nimport interpreter.ArithmeticValue\nimport interpreter.Value\nimport text.quote\n\nabstract class Ar"
  },
  {
    "path": "src/main/kotlin/semantic/types/Array.kt",
    "chars": 1251,
    "preview": "package semantic.types\n\ndata class ArrayType(var size: Int, val elementType: Type) : Type {\n    override fun sizeof(): I"
  },
  {
    "path": "src/main/kotlin/semantic/types/Enum.kt",
    "chars": 212,
    "preview": "package semantic.types\n\nimport interpreter.ArithmeticValue\n\nclass EnumerationConstant(val value: ArithmeticValue) : Type"
  },
  {
    "path": "src/main/kotlin/semantic/types/Function.kt",
    "chars": 1000,
    "preview": "package semantic.types\n\ndata class FunctionType(val returnType: Type, val parameters: List<Type>) : Type {\n    companion"
  },
  {
    "path": "src/main/kotlin/semantic/types/Pointer.kt",
    "chars": 1901,
    "preview": "package semantic.types\n\nimport interpreter.Value\n\ninterface ComparablePointerType : Type\n\ndata class PointerType(val ref"
  },
  {
    "path": "src/main/kotlin/semantic/types/Struct.kt",
    "chars": 1348,
    "preview": "package semantic.types\n\nimport semantic.Symbol\nimport syntax.lexer.Token\nimport syntax.lexer.missingIdentifier\n\nabstract"
  },
  {
    "path": "src/main/kotlin/semantic/types/Type.kt",
    "chars": 1552,
    "preview": "package semantic.types\n\nimport interpreter.Value\n\ninterface Type {\n    fun requiresStorage(): Boolean = true\n\n    fun is"
  },
  {
    "path": "src/main/kotlin/semantic/types/Typedef.kt",
    "chars": 277,
    "preview": "package semantic.types\n\nclass Typedef(val aliased: Type) : Type {\n    override fun requiresStorage(): Boolean = false\n\n "
  },
  {
    "path": "src/main/kotlin/semantic/types/Void.kt",
    "chars": 691,
    "preview": "package semantic.types\n\nobject VoidType : Type {\n    override fun pointer(): Type = VoidPointerType\n\n    override fun co"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/Characters.kt",
    "chars": 1363,
    "preview": "package syntax.lexer\n\nimport syntax.lexer.TokenKind.CHARACTER_CONSTANT\nimport syntax.lexer.TokenKind.STRING_LITERAL\n\nfun"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/Identifiers.kt",
    "chars": 837,
    "preview": "package syntax.lexer\n\nimport syntax.lexer.TokenKind.IDENTIFIER\n\ntailrec fun Lexer.identifierOrKeyword(): Token = when (n"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/Lexer.kt",
    "chars": 1254,
    "preview": "package syntax.lexer\n\nimport common.Diagnostic\n\nconst val EOF = '\\u0000'\n\nclass Lexer(private val input: String) {\n    v"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/NextToken.kt",
    "chars": 3526,
    "preview": "package syntax.lexer\n\nimport syntax.lexer.TokenKind.*\n\ntailrec fun Lexer.nextToken(): Token {\n    startAtIndex()\n    ret"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/Numbers.kt",
    "chars": 2381,
    "preview": "package syntax.lexer\n\nimport syntax.lexer.TokenKind.*\n\nfun Lexer.constant(): Token {\n    var seenDecimalPoint = false\n  "
  },
  {
    "path": "src/main/kotlin/syntax/lexer/SkipComments.kt",
    "chars": 332,
    "preview": "package syntax.lexer\n\nfun Lexer.skipSingleLineComment() {\n    while (next() != '\\n') {\n        if (current == EOF) retur"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/Token.kt",
    "chars": 1557,
    "preview": "package syntax.lexer\n\nimport common.Diagnostic\nimport syntax.lexer.TokenKind.IDENTIFIER\nimport syntax.lexer.TokenKind.ST"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/TokenKind.kt",
    "chars": 2334,
    "preview": "package syntax.lexer\n\nenum class TokenKind(val lexeme: String) {\n    ASSERT(\"assert\"),\n    AUTO(\"auto\"),\n    BREAK(\"brea"
  },
  {
    "path": "src/main/kotlin/syntax/lexer/TokenKindSet.kt",
    "chars": 2142,
    "preview": "package syntax.lexer\n\nimport java.lang.Long.lowestOneBit\nimport java.lang.Long.numberOfTrailingZeros\n\nprivate val TokenK"
  },
  {
    "path": "src/main/kotlin/syntax/parser/Autocompletion.kt",
    "chars": 1660,
    "preview": "package syntax.parser\n\nimport common.Diagnostic\nimport syntax.lexer.Lexer\nimport syntax.lexer.TokenKind\n\nfun autocomplet"
  },
  {
    "path": "src/main/kotlin/syntax/parser/Declarations.kt",
    "chars": 10650,
    "preview": "package syntax.parser\n\nimport semantic.enumStructUnion\nimport semantic.storageClasses\nimport semantic.typeSpecifierIdent"
  },
  {
    "path": "src/main/kotlin/syntax/parser/Expressions.kt",
    "chars": 3534,
    "preview": "package syntax.parser\n\nimport syntax.lexer.TokenKind\nimport syntax.lexer.TokenKind.*\nimport syntax.tree.*\n\nconst val PRE"
  },
  {
    "path": "src/main/kotlin/syntax/parser/ExternalDefinitions.kt",
    "chars": 1445,
    "preview": "package syntax.parser\n\nimport semantic.types.FunctionType\nimport syntax.lexer.TokenKind.*\nimport syntax.tree.*\n\nfun Pars"
  },
  {
    "path": "src/main/kotlin/syntax/parser/LeftDenotations.kt",
    "chars": 2263,
    "preview": "package syntax.parser\n\nimport syntax.lexer.Token\nimport syntax.lexer.TokenKind.*\nimport syntax.tree.*\n\nabstract class Le"
  },
  {
    "path": "src/main/kotlin/syntax/parser/NullDenotations.kt",
    "chars": 2981,
    "preview": "package syntax.parser\n\nimport syntax.lexer.Token\nimport syntax.lexer.TokenKind.*\nimport syntax.tree.*\n\nabstract class Nu"
  },
  {
    "path": "src/main/kotlin/syntax/parser/Parser.kt",
    "chars": 4029,
    "preview": "package syntax.parser\n\nimport common.Diagnostic\nimport semantic.SymbolTable\nimport syntax.lexer.Lexer\nimport syntax.lexe"
  },
  {
    "path": "src/main/kotlin/syntax/parser/Statements.kt",
    "chars": 2946,
    "preview": "package syntax.parser\n\nimport common.Diagnostic\nimport syntax.lexer.Token\nimport syntax.lexer.TokenKind.*\nimport syntax."
  },
  {
    "path": "src/main/kotlin/syntax/tree/DeclarationSpecifier.kt",
    "chars": 3241,
    "preview": "package syntax.tree\n\nimport semantic.types.Later\nimport semantic.types.Type\nimport syntax.lexer.Token\nimport syntax.lexe"
  },
  {
    "path": "src/main/kotlin/syntax/tree/Declarator.kt",
    "chars": 2368,
    "preview": "package syntax.tree\n\nimport semantic.types.Later\nimport semantic.types.Type\nimport syntax.lexer.Token\nimport syntax.lexe"
  },
  {
    "path": "src/main/kotlin/syntax/tree/Expression.kt",
    "chars": 4844,
    "preview": "package syntax.tree\n\nimport interpreter.Value\nimport semantic.Symbol\nimport semantic.types.Later\nimport semantic.types.T"
  },
  {
    "path": "src/main/kotlin/syntax/tree/External.kt",
    "chars": 1523,
    "preview": "package syntax.tree\n\nimport interpreter.BasicBlock\nimport semantic.types.StructType\nimport semantic.types.StructTypeLate"
  },
  {
    "path": "src/main/kotlin/syntax/tree/Node.kt",
    "chars": 505,
    "preview": "package syntax.tree\n\nimport syntax.lexer.Token\n\nabstract class Node {\n    fun walk(enter: (Node) -> Unit, leave: (Node) "
  },
  {
    "path": "src/main/kotlin/syntax/tree/Statement.kt",
    "chars": 3672,
    "preview": "package syntax.tree\n\nimport syntax.lexer.Token\n\nabstract class Statement : Node()\n\nclass Declaration(val specifiers: Dec"
  },
  {
    "path": "src/main/kotlin/text/Char.kt",
    "chars": 3556,
    "preview": "package text\n\nfun Char.quote(): String = when (this) {\n    '\\u0000' -> \"'\\\\0'\"\n    '\\u0007' -> \"'\\\\a'\"\n    '\\u0008' -> \""
  },
  {
    "path": "src/main/kotlin/text/String.kt",
    "chars": 169,
    "preview": "package text\n\nfun String.skipDigits(start: Int): Int {\n    val n = length\n    for (i in start until n) {\n        if (thi"
  },
  {
    "path": "src/main/kotlin/ui/Flexer.kt",
    "chars": 4455,
    "preview": "package ui\n\nimport common.puts\nimport freditor.FlexerState\nimport freditor.FlexerState.EMPTY\nimport freditor.FlexerState"
  },
  {
    "path": "src/main/kotlin/ui/MainFrame.kt",
    "chars": 18547,
    "preview": "package ui\n\nimport common.Diagnostic\nimport freditor.*\nimport interpreter.Interpreter\nimport interpreter.Memory\nimport s"
  },
  {
    "path": "src/main/kotlin/ui/MemoryUI.kt",
    "chars": 7163,
    "preview": "package ui\n\nimport common.Counter\nimport freditor.Fronts\nimport interpreter.Memory\nimport interpreter.PointerValue\nimpor"
  },
  {
    "path": "src/test/kotlin/interpreter/InterpreterTest.kt",
    "chars": 43840,
    "preview": "package interpreter\n\nimport org.junit.jupiter.api.Test\n\nclass InterpreterTest {\n    private fun run(program: String) {\n "
  },
  {
    "path": "src/test/kotlin/semantic/types/TypeToStringTest.kt",
    "chars": 4200,
    "preview": "package semantic.types\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass Ty"
  },
  {
    "path": "src/test/kotlin/syntax/lexer/LexerTest.kt",
    "chars": 4077,
    "preview": "package syntax.lexer\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.asser"
  },
  {
    "path": "src/test/kotlin/syntax/parser/AutocompletionTest.kt",
    "chars": 3404,
    "preview": "package syntax.parser\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass Aut"
  },
  {
    "path": "src/test/resources/junit-platform.properties",
    "chars": 107,
    "preview": "junit.jupiter.execution.parallel.enabled = true\njunit.jupiter.execution.parallel.mode.default = concurrent\n"
  }
]

About this extraction

This page contains the full source code of the fredoverflow/skorbut-release GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (292.6 KB), approximately 72.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!