Repository: effekt-lang/effekt
Pull request #495 (2024-06-08 11:05:30)

Recursive descent
A draft implementation of a recursive descent parser.

Currently missing:

- [x] not feature complete (many non-terminals are unimplemented)
- [x] positions are missing
- [ ] error reporting should be improved (original messages are in TODO comments)
- [ ] the parser for types requires quite a bit backtracking, this could be improved (also by changing the grammar itself)


Patch:
diff --git a/effekt/jvm/src/main/scala/effekt/Repl.scala b/effekt/jvm/src/main/scala/effekt/Repl.scala
index 15406ffe..d3f08efe 100644
--- a/effekt/jvm/src/main/scala/effekt/Repl.scala
+++ b/effekt/jvm/src/main/scala/effekt/Repl.scala
@@ -1,13 +1,14 @@
 package effekt
 
-import effekt.source._
-import effekt.context.{ Context, IOModuleDB }
-import effekt.symbols.{ BlockSymbol, DeclPrinter, Module, ValueSymbol, ErrorMessageInterpolator, isSynthetic }
-import effekt.util.{ AnsiColoredMessaging, AnsiHighlight, VirtualSource, getOrElseAborting }
+import effekt.lexer.Token
+import effekt.source.*
+import effekt.context.{Context, IOModuleDB}
+import effekt.symbols.{BlockSymbol, DeclPrinter, ErrorMessageInterpolator, Module, ValueSymbol, isSynthetic}
+import effekt.util.{AnsiColoredMessaging, AnsiHighlight, VirtualSource, getOrElseAborting}
 import effekt.util.messages.EffektError
 import effekt.util.Version.effektVersion
-import kiama.util.{ Console, REPL, Source, StringSource, Range }
-import kiama.parsing.{ NoSuccess, ParseResult, Success }
+import kiama.util.{Console, REPL, Range, Source, StringSource}
+import kiama.parsing.{NoSuccess, ParseResult, Success, Input}
 
 class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
 
@@ -53,8 +54,10 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
    * and everything else that can occur on the top-level.
    */
   override def parse(source: Source): ParseResult[Tree] = {
-    val parsers = new EffektParsers(positions)
-    parsers.parseAll(parsers.repl, source)
+    val lexer = effekt.lexer.Lexer(source)
+    val tokens = lexer.lex()
+    val parser = RecursiveDescent(context.positions, tokens, source)
+    parser.parseRepl(Input(source, 0))
   }
 
   /**
diff --git a/effekt/jvm/src/main/scala/effekt/Runner.scala b/effekt/jvm/src/main/scala/effekt/Runner.scala
index 5eb85f65..4202fe79 100644
--- a/effekt/jvm/src/main/scala/effekt/Runner.scala
+++ b/effekt/jvm/src/main/scala/effekt/Runner.scala
@@ -251,6 +251,7 @@ object LLVMRunner extends Runner[String] {
    * Assumes [[path]] has the format "SOMEPATH.ll".
    */
   override def build(path: String)(using C: Context): String =
+
     val out = C.config.outputPath()
     val basePath = (out / path.stripSuffix(".ll")).unixPath
     val llPath  = basePath + ".ll"
diff --git a/effekt/shared/src/main/scala/effekt/Lexer.scala b/effekt/shared/src/main/scala/effekt/Lexer.scala
new file mode 100644
index 00000000..58d9b985
--- /dev/null
+++ b/effekt/shared/src/main/scala/effekt/Lexer.scala
@@ -0,0 +1,600 @@
+package effekt.lexer
+
+import java.lang.Integer
+
+import scala.annotation.tailrec
+import scala.collection.mutable
+import scala.collection.immutable
+import scala.collection.BufferedIterator
+import scala.util.matching.Regex
+
+import effekt.context.Context
+
+import kiama.util.{ Source, Range }
+
+/** An error encountered during lexing a source string. */
+case class LexerError(msg: String, start: Int, end: Int) extends Throwable(msg)
+
+
+/**
+ * A token consist of the absolute starting position, the absolute end position in the source file
+ * and the kind of token. Both position are to be understood as inclusive.
+ */
+case class Token(start: Int, end: Int, kind: TokenKind)
+
+enum TokenKind {
+  // literals
+  case Integer(n: Long)
+  case Float(d: Double)
+  case Str(s: String, multiline: Boolean)
+  case Chr(c: Int)
+  // identifiers
+  case Ident(id: String)
+  // misc
+  case Comment(msg: String)
+  case Newline
+  case Space
+  case EOF
+  case Error(err: LexerError)
+  // symbols
+  case `=`
+  case `===`
+  case `!==`
+  case `:`
+  case `;`
+  case `::`
+  case `@`
+  case `${`
+  case `{`
+  case `}`
+  case `}$`
+  case `(`
+  case `)`
+  case `[`
+  case `]`
+  case `,`
+  case `'`
+  case `.`
+  case `/`
+  case `=>`
+  case `<>`
+  case `<`
+  case `<=`
+  case `>`
+  case `>=`
+  case `<{`
+  case `}>`
+  case `!`
+  case `|`
+  case `||`
+  case `&`
+  case `&&`
+  case `+`
+  case `++`
+  case `-`
+  case `*`
+  // keywords
+  case `let`
+  case `true`
+  case `false`
+  case `val`
+  case `var`
+  case `if`
+  case `else`
+  case `while`
+  case `type`
+  case `effect`
+  case `interface`
+  case `try`
+  case `with`
+  case `case`
+  case `do`
+  case `fun`
+  case `match`
+  case `def`
+  case `module`
+  case `import`
+  case `export`
+  case `extern`
+  case `include`
+  case `record`
+  case `box`
+  case `unbox`
+  case `return`
+  case `region`
+  case `resource`
+  case `new`
+  case `and`
+  case `is`
+  case `namespace`
+  case `pure`
+}
+object TokenKind {
+  // "Soft" keywords
+  val `resume` = TokenKind.Ident("resume")
+  val `in` = TokenKind.Ident("in")
+  val `at` = TokenKind.Ident("at")
+  val `__` = Ident("_")
+
+  def explain(kind: TokenKind): String = kind match {
+    case t if keywords.contains(t) => s"keyword ${kind}"
+    case Ident(n)          => s"identifier ${n}"
+    case Integer(n)        => s"integer ${n}"
+    case Float(d)          => s"float ${d}"
+    case Str(s, true)      => s"multi-line string"
+    case Str(s, false)     => s"string \"${s}\""
+    case Chr(c)            => s"character '${c}'"
+    case Comment(contents) => "comment"
+    case Newline           => "newline"
+    case Space             => "space"
+    case EOF               => "end of file"
+    case other             => other.toString
+  }
+
+  val keywords = Vector(
+    `let`, `true`, `false`, `val`, `var`, `if`, `else`, `while`, `type`, `effect`, `interface`,
+    `try`, `with`, `case`, `do`, `fun`, `match`, `def`, `module`, `import`, `export`, `extern`,
+    `include`, `record`, `box`, `unbox`, `return`, `region`, `resource`, `new`, `and`, `is`,
+    `namespace`, `pure`)
+
+}
+
+
+
+
+object Lexer {
+  import TokenKind.*
+
+  val keywordMap: immutable.HashMap[String, TokenKind] =
+    immutable.HashMap.from(TokenKind.keywords.map { case t => t.toString -> t })
+}
+
+/**
+ *
+ * @param source A string of a Effekt program to be lexed.
+ */
+class Lexer(source: Source) {
+  import Lexer.*
+
+  /** The absolute starting index in the source string of the currently scanned token */
+  var start: Int = 0
+  /** The absolute index of the lexer's reading 'head' in the source string. Example
+   * "hello world"
+   *   ^
+   *  current = 1
+   *  chars.next() = 'e'
+   */
+  var current: Int = 0
+  /** The sequence of tokens the source strings contains. Returned as the result by [[Lexer.run()]] */
+  val tokens: mutable.ListBuffer[Token] = mutable.ListBuffer.empty
+  // For future improvement, this is probably more performant than using substring on the source
+  // val currLexeme: mutable.StringBuilder = mutable.StringBuilder()
+  /** A peekable iterator of the source string. This is used instead of directly indexing the source string.
+  * This may only be advanced by using the `consume` function, otherwise positions cannot be keept track of.
+  */
+  val chars: BufferedIterator[Char] = source.content.iterator.buffered
+
+  val str: String = source.content
+
+  lazy val whitespace: Regex = """([ \t\r\n])""".r // single whitespace characters
+  val whitespaces = Set(' ', '\t', '\r', '\n')
+  val nameFirst = ('a'.to('z').iterator ++ 'A'.to('Z').iterator ++ List('_').iterator).toSet
+  val nameRest = ('a'.to('z').iterator ++ 'A'.to('Z').iterator ++ '0'.to('9').iterator ++ List('_', '!', '?', '$').iterator).toSet
+
+  def isEOF: Boolean =
+    current >= str.length
+
+  /** Advance the [[Lexer.chars]] iterator and [[Lexer.current]] index. Returns [[None]] if EOF is reached. */
+  def consume(): Option[Char] = {
+    if (!chars.hasNext) {
+      None
+    } else {
+      current += 1
+      Some(chars.next())
+    }
+  }
+
+  def expect(c: Char): Unit = {
+    val copt = consume()
+    copt match {
+      case Some(c1) =>
+        if (c == c1) ()
+        else err(s"Expected $c but found $c1 instead.")
+      case None => err(s"Expected $c but reached end of file.")
+    }
+
+  }
+
+  /** Advance the [[Lexer.chars]] iterator while the next character matches the given predicate. */
+  @tailrec
+  final def consumeWhile(pred: Char => Boolean): Unit =
+    peek() match {
+      case Some(c) if pred(c) => consume(); consumeWhile(pred)
+      case _ => ()
+    }
+
+  /** Peek at the next character. Returns [[None]] if EOF is reached. */
+  def peek(): Option[Char] = chars.headOption
+
+  def peekMatches(pred: Char => Boolean): Boolean = peek().exists(pred)
+
+  def peekN(lookahead: Int): Option[Char] = {
+    if (current - 1 + lookahead < str.length) Some(str.charAt(current - 1 + lookahead))
+    else None
+  }
+
+  /** Convenience function for creating a token.
+   * Assumed Invariant: When making a token, the lexer's head [[current]] is pointing not at the token's
+   * last position but at the next one.
+   */
+  def makeToken(kind: TokenKind): Token =
+    Token(start, current - 1, kind)
+
+  /** Used for reporting erros while lexing. These are only caught by the `lex` function. */
+  def err(msg: String, start: Int = start, end: Int = current): Nothing =
+    throw LexerError(msg, start, end)
+
+  /** Checks if the characters starting at [[start]] match the expected string and only then
+   * consumes all corresponding characters. That is, if there's no match, no characters are consumed.
+   **/
+  def matches(expected: String, start: Int = start): Boolean = {
+    val len = expected.length
+    val end = start + len - 1
+    if (end >= str.length) return false
+    val slice = str.substring(start, end + 1)
+    if (slice == expected) {
+      (start to end).foreach(_ => consume())
+      true
+    } else {
+      false
+    }
+  }
+
+  /** Extract a slice of the source string delimited by the starting and (exclusive) current index */
+  def slice(start: Int = start, end: Int = current): String =
+    str.substring(start, end)
+
+  /** Like [[Lexer.matches]] but starts matching at the lexer's current head [[current]]. */
+  def nextMatches(expected: String): Boolean = matches(expected, current)
+
+  def nextMatches(expected: Char): Boolean =
+    peek().exists { c =>
+      val eq = c == expected
+      if (eq) consume()
+      eq
+    }
+
+  def matchesRegex(r: Regex): Boolean = {
+    val rest = str.substring(start)
+    val candidate = rest.takeWhile { c => !whitespace.matches(c.toString) }
+    r.matches(candidate)
+  }
+
+  def lex()(using ctx: Context): Vector[Token] =
+    try {
+      run()
+    } catch {
+      case LexerError(msg, start, end) => {
+        val relativeStart = source.offsetToPosition(start)
+        val relativeEnd = source.offsetToPosition(end)
+        val range = Range(relativeStart, relativeEnd)
+        ctx.abort(effekt.util.messages.ParseError(msg, Some(range)))
+      }
+    }
+
+  /** Main entry-point of the lexer. Whitespace is ignored and comments are collected.
+   * If an error is encountered, all successfully scanned tokens this far will returned,
+   * including the error.
+   */
+  def run(): Vector[Token] = {
+    var err: Option[LexerError] = None
+    var eof = false
+    while (!eof && err.isEmpty) {
+      val kind =
+        // If the last token was `}` and we are currently inside unquotes, remember to directly continue
+        // lexing a string
+        if (tokens.lastOption.map { _.kind }.contains(TokenKind.`}`) && delimiters.pop() == `${{`) {
+          // TODO: catch error
+          val delim = delimiters.pop().asInstanceOf[StrDelim]
+          matchString(delim, true)
+        } else nextToken()
+      kind match {
+        case TokenKind.EOF =>
+          eof = true
+          // EOF has the position of one after the last character
+          // this may be useful for a streaming lexer
+          tokens += Token(current + 1, current + 1, TokenKind.EOF)
+        case k =>
+          tokens += makeToken(k)
+      }
+      start = current
+    }
+    tokens.toVector
+  }
+
+  // --- Literal and comment matchers ---
+  // It is assumed that the prefix character which was used for deciding on the kind of the token is already consumed.
+
+  /** Matches a number literal -- both floats and integers. However, signs are not lexed but instead
+   * are to be handled by the parser later on.
+   */
+  def matchNumber(): TokenKind = {
+    consumeWhile(_.isDigit)
+    peekN(1) match {
+      case Some('.') => {
+        peekN(2) match {
+          case Some(c) if c.isDigit =>
+            consume()
+            consumeWhile(_.isDigit)
+            slice().toDoubleOption match {
+              case None => err("Not a 64bit floating point literal.")
+              case Some(n) => TokenKind.Float(n)
+            }
+          case _ => TokenKind.Integer(slice().toInt)
+        }
+      }
+      case _ =>
+        slice().toLongOption match {
+          case None => err("Not a 64bit integer literal.")
+          case Some(n) => TokenKind.Integer(n)
+        }
+    }
+  }
+
+  /** This is for remembering delimiters of strings and unquotes for string interpolation (`${`, `}`).
+  * If we encounter a `"` or `"""`, we push it onto the stack and pop from the stack if we see such
+  * delimiters again while lexing a string.
+  * Likewise, if we are at a `${`, push it onto the stack and pop from the stack upon seeing a `}`.
+  */
+  val delimiters = mutable.Stack[Delimiter]()
+  sealed trait Delimiter
+  sealed trait InterpolateDelim extends Delimiter
+  case object `${{` extends InterpolateDelim
+  case object `{{` extends InterpolateDelim
+  sealed trait StrDelim extends Delimiter {
+    def offset: Int =
+      this match {
+        case `"` => 1
+        case `"""` => 3
+      }
+
+    override def toString: String =
+      this match {
+        case `"` => "\""
+        case `"""` => "\"\"\""
+      }
+
+    def isMultiline: Boolean =
+      this match {
+        case `"` => false
+        case `"""` => true
+      }
+  }
+  case object `"` extends StrDelim
+  case object `"""` extends StrDelim
+
+  /** Matches a string literal -- both single- and multi-line strings.
+   * Strings may contain space characters (e.g. \n, \t, \r), which are stored unescaped for single-line strings and
+   * escaped for multi-line strings.
+   * Strings may also contain unquotes, i.e., "f = ${x + 1}" that include arbitrary expressions.
+   */
+  def matchString(delim: StrDelim, continued: Boolean = false): TokenKind = {
+    if (nextMatches(delim.toString)) return TokenKind.Str("", delim.isMultiline)
+    val offset = delim.offset
+    val st = if (continued) start else start + offset
+    var endString = false
+    delimiters.push(delim)
+    while (!endString) {
+      peek() match {
+        case Some('\"') => {
+          consume()
+          delim match {
+            case `"""` => {
+              if (peekN(1).contains('\"') && peekN(2).contains('\"')) {
+                consume(); consume()
+                delimiters.pop()
+                endString = true
+              }
+            }
+            case `"` => {
+              delimiters.pop()
+              endString = true
+            }
+          }
+        }
+        case Some('\n') if delim == `"` =>
+          return err("Linebreaks are not allowed in single-line strings.")
+        // handle escape sequences
+        case Some('\\') => {
+          expect('\\')
+          peek() match {
+            case Some('\"') | Some('\\') | Some('n') | Some('t') | Some('r') => consume()
+            case Some('u') => {
+              consume()
+              consumeWhile(c => !c.isWhitespace && c != '"')
+            }
+            case _ => err("Invalid escape sequence.", current - 1, current)
+          }
+        }
+        case Some('$') if peekN(2).contains('{') => {
+          val s = slice(st, current)
+          return TokenKind.Str(s, delim.isMultiline)
+        }
+        case None =>
+          return err("Unterminated string.", start, start + offset)
+        case _ =>
+          consume()
+      }
+    }
+    val s = slice(st, current - offset)
+
+    val stringContent =
+      if (delim.isMultiline) {
+        // Do not unescape multiline strings at all for multiline strings (see comments in PR #495)
+        s
+      } else {
+        // Unescape the following sequences: control: `\b`, `\t`, `\n`, `\f`, `\r`; escape:  `\\`, `\"`, `\'`
+        // For now, we only support a subset determined by `StringContext.processEscapes`
+        try {
+          StringContext.processEscapes(s)
+        } catch {
+          case e: StringContext.InvalidEscapeException => err("Contains invalid escape sequence.", e.index, e.index)
+        }
+      }
+
+    TokenKind.Str(stringContent, delim.isMultiline)
+  }
+
+  /** Matches a character: '.+' */
+  def matchChar(): TokenKind = {
+    if (peekMatches(_ == '\'')) err("Empty character literal.")
+    consumeWhile(_ != '\'')
+    expect('\'')
+    // exclude opening and closing '
+    val cs = slice(start + 1, current - 1)
+    TokenKind.Chr(cs.codePointAt(0))
+  }
+
+  lazy val hexadecimal = ('a' to 'f') ++ ('A' to 'F') ++ ('0' to '9')
+
+  /** \u<HEXADECIMAL>+ */
+  def matchUnicodeLiteral(): TokenKind = {
+    consumeWhile(hexadecimal.contains(_))
+    val n = slice(start + 2, current)
+    try {
+      TokenKind.Chr(Integer.parseInt(n, 16))
+    } catch {
+      case e: Throwable => err("Invalid unicode literal.")
+    }
+  }
+
+  /** Matches a mult-line comment delimited by /* and */. */
+  def matchMultilineComment(): TokenKind = {
+    var closed = false
+    while (!closed) {
+      consume() match {
+        // end comment on */
+        case Some('*') if nextMatches('/') => closed = true
+        case Some(_) => ()
+        // reached EOF without encountering */
+        case None => return err("Unterminated multi-line comment; expected closing `*/`.")
+      }
+    }
+    // exclude /* and */ from the comment's content
+    val comment = slice(start + 2, current - 2)
+    TokenKind.Comment(comment)
+  }
+
+  /** Matches a single-line comment delimited by // and a newline. */
+  def matchComment(): TokenKind = {
+    var reachedNewline = false
+    while (!reachedNewline) {
+      // peek to ensure the newline is not part of the comment
+      peek() match {
+        // comment is terminated when encountering a new line
+        case Some('\n') => reachedNewline = true
+        case None => reachedNewline = true
+        case _ => consume()
+      }
+    }
+    // exclude //
+    val comment = slice(start + 2, current)
+    TokenKind.Comment(comment)
+  }
+
+  @tailrec
+  final def matchWhitespaces(): Unit = {
+      peek() match {
+        case Some(' ') | Some('\t') => {
+          consume()
+          // currently, we ignore any whitespace other than newlines
+          // tokens += makeToken(TokenKind.Space)
+          matchWhitespaces()
+        }
+        case Some('\n') => {
+          consume()
+          tokens += makeToken(TokenKind.Newline)
+          matchWhitespaces()
+        }
+        // windows, of course, has to do things differently. A newline there is represented by \r\n
+        case Some('\r') => {
+          consume()
+          nextMatches("\n")
+          tokens += makeToken(TokenKind.Newline)
+          matchWhitespaces()
+        }
+        case _ => start = current
+      }
+  }
+
+  def nextToken(): TokenKind = {
+    import TokenKind.*
+    matchWhitespaces()
+    val maybeChar = consume()
+    if (maybeChar.isEmpty) return EOF
+    val c = maybeChar.get
+    c match {
+      // --- symbols & pre- and infix operations ---
+      case '=' if nextMatches('>') => `=>`
+      case '=' if nextMatches('=') => `===`
+      case '=' => `=`
+      case ':' if nextMatches(':') => `::`
+      case ':' => `:`
+      case ';' => `;`
+      case '@' => `@`
+      case '<' if nextMatches('{') => `<{`
+      case '<' if nextMatches('>') => `<>`
+      case '<' if nextMatches('=') => `<=`
+      case '<' => `<`
+      case '>' if nextMatches('=') => `>=`
+      case '>' => `>`
+      case '{' => {
+        delimiters.push(`{{`)
+        `{`
+      }
+      case '}' if nextMatches('>') => `}>`
+      case '}' => `}`
+      case '(' => `(`
+      case ')' => `)`
+      case '[' => `[`
+      case ']' => `]`
+      case ',' => `,`
+      case '.' => `.`
+      case '/' if nextMatches('*') => matchMultilineComment()
+      case '/' if nextMatches('/') => matchComment()
+      case '/' => `/`
+      case '!' if nextMatches('=') => `!==`
+      case '!' => `!`
+      case '|' if nextMatches('|') => `||`
+      case '|' => `|`
+      case '&' if nextMatches('&') => `&&`
+      case '&' => `&`
+      case '*' => `*`
+      case '+' if nextMatches('+') => `++`
+      case '+' => `+`
+      case '-' if peek().exists(_.isDigit) => matchNumber()
+      case '-' => `-`
+      case '$' if nextMatches('{') => {
+        delimiters.push(`${{`)
+        TokenKind.`${`
+      }
+      case '$' => TokenKind.`${`
+      // --- literals ---
+      case '\'' => matchChar()
+      case '\\' if nextMatches("u") => matchUnicodeLiteral()
+      case '\"' if nextMatches("\"\"") => matchString(`"""`)
+      case '\"' => matchString(`"`)
+      case c if c.isDigit => matchNumber()
+      // --- keywords & identifiers ---
+      case c if nameFirst.contains(c) => {
+        // since keywords are a subclass of identifiers and we want to match either keywords or identifiers,
+        // we look for valid names
+        consumeWhile { c => nameRest.contains(c) }
+        val s = slice()
+        // check if the slice matches any know keyword, otherwise it is necessarily an identifier
+        keywordMap.getOrElse(s, TokenKind.Ident(s))
+      }
+      case c => err(s"Invalid keyword/identifier: $c.")
+    }
+  }
+}
diff --git a/effekt/shared/src/main/scala/effekt/Parser.scala b/effekt/shared/src/main/scala/effekt/Parser.scala
index 3f800789..677b07c6 100644
--- a/effekt/shared/src/main/scala/effekt/Parser.scala
+++ b/effekt/shared/src/main/scala/effekt/Parser.scala
@@ -2,12 +2,11 @@ package effekt
 
 import effekt.context.Context
 import effekt.source.*
-import effekt.util.{SourceTask, VirtualSource}
+import effekt.util.{ VirtualSource }
 import effekt.util.messages.ParseError
-import kiama.parsing.{Failure, Input, NoSuccess, ParseResult, Parsers, Success}
-import kiama.util.{Position, Positions, Range, Source, StringSource}
+import kiama.parsing.{ Failure, Input, NoSuccess, ParseResult, Parsers, Success }
+import kiama.util.{ Position, Positions, Range, Source, StringSource }
 
-import scala.util.matching.Regex
 import scala.language.implicitConversions
 
 /**
@@ -19,21 +18,22 @@ object Parser extends Phase[Source, Parsed] {
 
   val phaseName = "parser"
 
-  /**
-   * Note: The parser needs to be created freshly since otherwise the memo tables will maintain wrong results for
-   * new input sources. Even though the contents differ, two sources are considered equal since only the paths are
-   * compared.
-   */
-  def parser(implicit C: Context) = new EffektParsers(C.positions)
-
-  def run(source: Source)(implicit C: Context) = source match {
+  def run(source: Source)(implicit C: Context): Option[PhaseResult.Parsed] = source match {
     case VirtualSource(decl, _) => Some(decl)
     case source =>
       //println(s"parsing ${source.name}")
-      Context.timed(phaseName, source.name) { parser.parse(source) }
-  } map { tree => Parsed(source, tree) }
+      Context.timed(phaseName, source.name) {
+        val lexer = effekt.lexer.Lexer(source)
+        val tokens = lexer.lex()
+        val parser = RecursiveDescent(C.positions, tokens, source)
+        parser.parse(Input(source, 0))
+      }
+  } map { tree =>
+    Parsed(source, tree)
+  }
 }
 
+
 /**
  * TODO at the moment parsing is still very slow. I tried to address this prematurily
  * by adding cuts and using PackratParser for nonterminals. Maybe moving to a separate lexer phase
@@ -353,7 +353,7 @@ class EffektParsers(positions: Positions) extends EffektLexers(positions) {
     )
 
   lazy val valDef: P[Def] =
-     `val` ~> idDef ~ (`:` ~/> valueType).? ~ (`=` ~/> stmt) ^^ ValDef.apply
+    `val` ~> idDef ~ (`:` ~/> valueType).? ~ (`=` ~/> stmt) ^^ ValDef.apply
 
   lazy val varDef: P[Def] =
     `var` ~/> idDef ~ (`:` ~/> valueType).? ~ (`in` ~/> idRef).? ~ (`=` ~/> stmt) ^^ {
@@ -923,7 +923,6 @@ class EffektLexers(positions: Positions) extends Parsers(positions) {
   lazy val integerLiteral  = regex("([-+])?(0|[1-9][0-9]*)".r, s"Integer literal")
   lazy val doubleLiteral   = regex("([-+])?(0|[1-9][0-9]*)[.]([0-9]+)".r, "Double literal")
   lazy val stringLiteral   = regex("""\"(\\.|\\[\r?\n]|[^\r\n\"])*+\"""".r, "String literal")
-  // TODO add escape sequences
   lazy val charLiteral   = regex("""'.'""".r, "Character literal") ^^ { s => s.codePointAt(1) }
   lazy val unicodeChar   = regex("""\\u\{[0-9A-Fa-f]{1,6}\}""".r, "Unicode character literal") ^^ {
     case contents =>  Integer.parseInt(contents.stripPrefix("\\u{").stripSuffix("}"), 16)
diff --git a/effekt/shared/src/main/scala/effekt/RecursiveDescent.scala b/effekt/shared/src/main/scala/effekt/RecursiveDescent.scala
new file mode 100644
index 00000000..4c117084
--- /dev/null
+++ b/effekt/shared/src/main/scala/effekt/RecursiveDescent.scala
@@ -0,0 +1,1365 @@
+package effekt
+
+import effekt.lexer.*
+import effekt.lexer.TokenKind.{ `::` as PathSep, * }
+import effekt.source.*
+import effekt.context.Context
+import kiama.parsing.{ ParseResult, Input }
+import kiama.util.{ Positions, Source, StringSource, Range, Position }
+
+import scala.annotation.{ tailrec, targetName }
+import scala.util.matching.Regex
+import scala.language.implicitConversions
+import scala.util.boundary
+import scala.util.boundary.break
+
+
+case class Fail(message: String, position: Int) extends Throwable(null, null, false, false)
+
+class RecursiveDescent(positions: Positions, tokens: Seq[Token], source: Source) {
+
+  import scala.collection.mutable.ListBuffer
+
+  def parse(input: Input)(using C: Context): Option[ModuleDecl] =
+
+    try {
+      //println(input.tokens)
+      //val before = System.currentTimeMillis()
+      val res = Some(program())
+      //val after = System.currentTimeMillis()
+      //println(s"${input.source.name}: ${after - before}ms")
+
+      res
+    } catch {
+      case Fail(msg, pos) =>
+        val source = input.source
+        val range = tokens.lift(pos) match {
+          case Some(value) =>
+            val from = source.offsetToPosition(value.start)
+            val to = source.offsetToPosition(value.end + 1)
+            Some(Range(from, to))
+          case None =>
+            val pos = Position(0, 0, source)
+            Some(Range(pos, pos))
+        }
+
+        C.report(effekt.util.messages.ParseError(msg, range)) // fix error reporting
+        None
+    }
+
+  // here we need to convert to kiamas error format since REPL is defined in kiama
+  def parseRepl(input: Input)(using C: Context): ParseResult[Tree] =
+    try { kiama.parsing.Success(repl(), input) } catch {
+      case Fail(msg, pos) => kiama.parsing.Error(msg, input.copy(offset = pos))
+    }
+
+
+  // Interfacing with the token stream
+  // ---------------------------------
+
+  // always points to the latest non-space position
+  var position: Int = 0
+
+  def peek: Token = tokens(position)
+
+  /**
+   * Negative lookahead
+   */
+  def lookbehind(offset: Int): Token =
+    tokens(position - offset)
+
+  /**
+   * Peeks n tokens ahead of the current one.
+   */
+  def peek(offset: Int): Token =
+    @tailrec
+    def go(position: Int, offset: Int): Token =
+      if position >= tokens.length then fail("Unexpected end of file")
+
+      tokens(position) match {
+        case token if isSpace(token.kind) => go(position + 1, offset)
+        case token if offset <= 0 => token
+        case _ => go(position + 1, offset - 1)
+      }
+
+    go(position, offset)
+
+  // the previously consumed token
+  var previous = tokens(position)
+
+  def peek(kind: TokenKind): Boolean =
+    peek.kind == kind
+  def peek(offset: Int, kind: TokenKind): Boolean =
+    peek(offset).kind == kind
+
+  def hasNext(): Boolean = position < tokens.length
+  def next(): Token =
+    val t = tokens(position)
+    skip()
+    t
+
+  /**
+   * Skips the current token and then all subsequent whitespace
+   */
+  def skip(): Unit =
+    previous = tokens(position)
+    position += 1;
+    spaces()
+
+  def isSpace(kind: TokenKind): Boolean =
+    kind match {
+      case TokenKind.Space | TokenKind.Comment(_) | TokenKind.Newline => true
+      case _ => false
+    }
+
+  @tailrec
+  final def spaces(): Unit = if hasNext() then peek.kind match {
+    case kind if isSpace(kind) => position += 1; spaces()
+    case _ => ()
+  }
+
+  def consume(kind: TokenKind): Unit =
+    // if !hasNext() then fail(s"Expected ${kind}, but reached end of file")
+    val positionBefore = position
+    val t = next()
+
+    if (t.kind != kind) {
+      // we need to fail at the position before consuming
+      position = positionBefore
+      fail(s"Expected ${explain(kind)} but got ${explain(t.kind)}")
+    }
+
+
+  /* The actual parser itself
+  * ------------------------
+  * We use the following naming conventions for nonterminals:
+  *
+  *  - maybe[...]s: zero or more times
+  *  - [...]s: one or more times
+  *  - [...]opt: optional type annotation
+  *
+  * Furthermore, we try to adhere to the following conventions:
+  * - Use `backtrack` with caution and as a last resort. Try to disambiguate the grammar by using `peek` and also try to
+  *   only use `backtrack` on "shallow" non-terminals, that is, not on a non-terminal containing an expression,
+  *   a statement or a type.
+  * - For the same reason, there is no function `manyWhile[T](p: => T): List[T]` but only 
+  *   `manyWhile[T](p: => T, predicate: => Boolean): List[T]` as this one does not use backtracking.
+  * - Use `fail` for reporting errors.
+  * - Functions consuming tokens have an empty parameter list `()`, functions that do not, have no parameter list (e.g. peek)
+  * - All non-terminals are to use the `nonterminal` function for keeping track of positions.
+  */
+
+  // tokens that delimit a statement
+  def returnPosition: Boolean = peek(`}`) || peek(`case`) || peek(`}>`) || peek(EOF)
+
+  /**
+   * Statements
+   */
+  def stmts(): Stmt =
+    nonterminal:
+      peek.kind match {
+        case `val`  => valStmt()
+        case _ if isDefinition => DefStmt(definition(), semi() ~> stmts())
+        case `with` => withStmt()
+        case `var`  => DefStmt(varDef(), semi() ~> stmts())
+        case `return` =>
+          val result = `return` ~> Return(expr())
+          maybeSemi()
+          result
+        case _ =>
+          val e = expr()
+          semi()
+          if returnPosition then Return(e)
+          else ExprStmt(e, stmts())
+      }
+
+  // ATTENTION: here the grammar changed (we added `with val` to disambiguate)
+  // with val <ID> (: <TYPE>)? = <EXPR>; <STMTS>
+  // with val (<ID> (: <TYPE>)?...) = <EXPR>
+  // with <EXPR>; <STMTS>
+  def withStmt(): Stmt = `with` ~> peek.kind match {
+    case `val` =>
+      val params = (`val` ~> peek.kind match {
+        case `(` => valueParamsOpt()
+        case _ => List(valueParam()) // TODO copy position
+      })
+      desugarWith(params, `=` ~> expr(), semi() ~> stmts())
+
+    case _ => desugarWith(Nil, expr(), semi() ~> stmts())
+  }
+
+  def desugarWith(params: List[ValueParam], call: Term, body: Stmt): Stmt = call match {
+     case m@MethodCall(receiver, id, tps, vargs, bargs) =>
+       Return(MethodCall(receiver, id, tps, vargs, bargs :+ (BlockLiteral(Nil, params, Nil, body))))
+     case c@Call(callee, tps, vargs, bargs) =>
+       Return(Call(callee, tps, vargs, bargs :+ (BlockLiteral(Nil, params, Nil, body))))
+     case Var(id) =>
+       val tgt = IdTarget(id)
+       Return(Call(tgt, Nil, Nil, (BlockLiteral(Nil, params, Nil, body)) :: Nil))
+     case term =>
+       Return(Call(ExprTarget(term), Nil, Nil, (BlockLiteral(Nil, params, Nil, body)) :: Nil))
+  }
+
+  def maybeSemi(): Unit = if isSemi then semi()
+  def isSemi: Boolean = peek.kind match {
+    // \n   ; while
+    //
+    case `;` => true
+    // foo }
+    //     ^
+    case t if returnPosition => true
+
+    // \n   while
+    //      ^
+    case _ => lookbehind(1).kind == Newline
+  }
+  def semi(): Unit = peek.kind match {
+    // \n   ; while
+    //
+    case `;` => consume(`;`)
+    // foo }
+    //     ^
+    case t if returnPosition => ()
+
+    // \n   while
+    //      ^
+    case _ if lookbehind(1).kind == Newline => ()
+
+    case _ => fail("Expected ;")
+  }
+
+  def stmt(): Stmt =
+    nonterminal:
+      if peek(`{`) then braces { BlockStmt(stmts()) }
+      else when(`return`) { Return(expr()) } { Return(expr()) }
+
+
+  /**
+   * Main entry point for the repl.
+   */
+  def repl(): Tree =
+    nonterminal:
+       // skip spaces at the start
+       spaces()
+       val res = peek.kind match {
+         case t if isToplevel => toplevel()
+         case `import` => includeDecl()
+         case _ => expr()
+       }
+       if peek(`EOF`) then res else fail("Unexpected end of input")
+
+  /**
+   * Main entry point
+   */
+   def program(): ModuleDecl =
+     nonterminal:
+       // skip spaces at the start
+       spaces()
+       val res = ModuleDecl(moduleDecl(), manyWhile(includeDecl(), `import`), toplevelDefs())
+       if peek(`EOF`) then res else fail("Unexpected end of input")
+       // failure("Required at least one top-level function or effect definition")
+
+  def moduleDecl(): String =
+    when(`module`) { moduleName() } { defaultModulePath }
+
+  // we are purposefully not using File here since the parser needs to work both
+  // on the JVM and in JavaScript
+  def defaultModulePath: String =
+    val baseWithExt = source.name.split("[\\\\/]").last
+    baseWithExt.split('.').head
+
+
+  def includeDecl(): Include =
+    nonterminal:
+      Include(`import` ~> moduleName())
+
+  def moduleName(): String =
+    some(ident, `/`).mkString("/")
+
+  def isToplevel: Boolean = peek.kind match {
+    case `val` | `fun` | `def` | `type` | `effect` | `namespace` |
+         `extern` | `effect` | `interface` | `type` | `record` => true
+    case _ => false
+  }
+
+  def toplevel(): Def =
+    nonterminal:
+      peek.kind match {
+        case `val`       => valDef()
+        case `def`       => defDef()
+        case `interface` => interfaceDef()
+        case `type`      => typeOrAliasDef()
+        case `record`    => recordDef()
+        case `extern`    => externDef()
+        case `effect`    => effectOrOperationDef()
+        case `namespace` => namespaceDef()
+        case `var`       => fail("Mutable variable declarations are currently not supported on the toplevel.")
+        case _ => fail("Expected a top-level definition")
+      }
+
+  def toplevelDefs(): List[Def] =
+    peek.kind match {
+      case `namespace` =>
+        consume(`namespace`)
+        val id = idDef()
+        peek.kind match {
+          case `{` =>
+            val defs = braces(toplevelDefs())
+            val df = toplevelDefs()
+            NamespaceDef(id, defs) :: df
+          case _   =>
+            val defs = toplevelDefs()
+            List(NamespaceDef(id, defs))
+        }
+      case _ =>
+        if (isToplevel) toplevel() :: toplevelDefs()
+        else Nil
+    }
+
+  def toplevels(): List[Def] =
+    nonterminal:
+      manyWhile(toplevel(), isToplevel)
+
+  def isDefinition: Boolean = peek.kind match {
+    case `val` | `def` | `type` | `effect` | `namespace` => true
+    case `extern` | `effect` | `interface` | `type` | `record` =>
+      val kw = peek.kind
+      fail(s"Only supported on the toplevel: ${kw.toString} declaration.")
+    case _ => false
+  }
+
+  def definition(): Def =
+    nonterminal:
+      peek.kind match {
+        case `val`       => valDef()
+        case `def`       => defDef()
+        case `type`      => typeOrAliasDef()
+        case `effect`    => effectDef()
+        case `namespace` => namespaceDef()
+        // TODO
+        //     (`extern` | `effect` | `interface` | `type` | `record`).into { (kw: String) =>
+        //        failure(s"Only supported on the toplevel: ${kw} declaration.")
+        //      }
+        case _ => fail("Expected definition")
+      }
+
+  def definitions(): List[Def] =
+    nonterminal:
+      manyWhile(definition(), isDefinition)
+
+  def functionBody: Stmt = stmt() // TODO error context: "the body of a function definition"
+
+  def valDef(): Def =
+    nonterminal:
+      ValDef(`val` ~> idDef(), maybeValueTypeAnnotation(), `=` ~> stmt())
+
+  /**
+   * In statement position, val-definitions can also be destructing:
+   *   i.e. val (l, r) = point(); ...
+   */
+  def valStmt(): Stmt =
+    nonterminal:
+      val startMarker = nonterminal { new {} }
+      def simpleLhs() = backtrack {
+        `val` ~> idDef() ~ maybeValueTypeAnnotation() <~ `=`
+      } map {
+        case id ~ tpe =>
+          val binding = stmt()
+          val valDef = ValDef(id, tpe, binding).withRangeOf(startMarker, binding)
+          DefStmt(valDef, { semi(); stmts() })
+      }
+      def matchLhs() =
+        `val` ~> matchPattern() ~ manyWhile(`and` ~> matchGuard(), `and`) <~ `=` match {
+          case AnyPattern(id) ~ Nil =>
+            val binding = stmt()
+            val valDef = ValDef(id, None, binding).withRangeOf(startMarker, binding)
+            DefStmt(valDef, { semi(); stmts() })
+          case p ~ guards =>
+            val sc = expr()
+            val default = when(`else`) { Some(stmt()) } { None }
+            val body = semi() ~> stmts()
+            val clause = MatchClause(p, guards, body).withRangeOf(p, sc)
+            val matching = Match(sc, List(clause), default).withRangeOf(startMarker, sc)
+            Return(matching)
+        }
+
+      simpleLhs() getOrElse matchLhs()
+
+
+  def varDef(): Def =
+    nonterminal:
+      (`var` ~> idDef()) ~ maybeValueTypeAnnotation() ~ when(`in`) { Some(idRef()) } { None } ~ (`=` ~> stmt()) match {
+        case id ~ tpe ~ Some(reg) ~ expr => RegDef(id, tpe, reg, expr)
+        case id ~ tpe ~ None ~ expr      => VarDef(id, tpe, expr)
+      }
+
+  def defDef(): Def =
+    nonterminal:
+      val id = consume(`def`) ~> idDef()
+
+      def isBlockDef: Boolean = peek(`:`) || peek(`=`)
+
+      if isBlockDef then
+        // (: <VALUETYPE>)? `=` <EXPR>
+        DefDef(id, maybeBlockTypeAnnotation(), `=` ~> expr())
+      else
+        // [...](<PARAM>...) {...} `=` <STMT>>
+        val (tps, vps, bps) = params()
+        FunDef(id, tps, vps, bps, maybeReturnAnnotation(), `=` ~> stmt())
+
+
+  // right now: data type definitions (should be renamed to `data`) and type aliases
+  def typeOrAliasDef(): Def =
+    nonterminal:
+      val id ~ tps = (`type` ~> idDef()) ~ maybeTypeParams()
+
+      peek.kind match {
+        case `=` => `=` ~> TypeDef(id, tps, valueType())
+        case _ => braces { DataDef(id, tps, manyWhile({ constructor() <~ semi() }, !peek(`}`))) }
+      }
+
+  def recordDef(): Def =
+    nonterminal:
+      RecordDef(`record` ~> idDef(), maybeTypeParams(), valueParams())
+
+  def constructor(): Constructor =
+    nonterminal:
+      Constructor(idDef(), maybeTypeParams(), valueParams())
+
+  // On the top-level both
+  //    effect Foo = {}
+  // and
+  //    effect Foo(): Int
+  // are allowed. Here we simply backtrack, since effect definitions shouldn't be
+  // very long and cannot be nested.
+  def effectOrOperationDef(): Def =
+    nonterminal:
+      backtrack { effectDef() } getOrElse { operationDef() }
+
+  def effectDef(): Def =
+    nonterminal:
+      // effect <NAME> = <EFFECTS>
+      EffectDef(`effect` ~> idDef(), maybeTypeParams(), `=` ~> effects())
+
+  // effect <NAME>[...](...): ...
+  def operationDef(): Def =
+    nonterminal:
+      `effect` ~> operation() match {
+        case op =>
+          // TODO is the `true` flag used at all anymore???
+          InterfaceDef(IdDef(op.id.name), Nil, List(op), true)
+      }
+
+  def operation(): Operation =
+    nonterminal:
+      idDef() ~ params() ~ returnAnnotation() match {
+        case id ~ (tps, vps, bps) ~ ret => Operation(id, tps, vps, bps, ret)
+      }
+
+  def interfaceDef(): InterfaceDef =
+    nonterminal:
+      InterfaceDef(`interface` ~> idDef(), maybeTypeParams(), `{` ~> manyWhile(`def` ~> operation(), `def`) <~ `}`, true)
+
+  def namespaceDef(): Def =
+    nonterminal:
+      consume(`namespace`)
+      val id = idDef()
+      // namespace foo { <DEFINITION>* }
+      if peek(`{`) then braces { NamespaceDef(id, definitions()) }
+      // namespace foo
+      // <DEFINITION>*
+      else { semi(); NamespaceDef(id, definitions()) }
+
+
+  def externDef(): Def =
+    nonterminal:
+      { peek(`extern`); peek(1).kind } match {
+        case `type`      => externType()
+        case `interface` => externInterface()
+        case `resource`  => externResource()
+        case `include`   => externInclude()
+        // extern """..."""
+        case s: Str      => externString()
+        case Ident(_) | `pure` =>
+          // extern IDENT def ...
+          if (peek(2, `def`)) externFun()
+          // extern IDENT """..."""
+          else externString()
+        // extern {...} def ...
+        case _ => externFun()
+      }
+
+  def featureFlag(): FeatureFlag =
+    next().kind match {
+      case Ident("default") => FeatureFlag.Default
+      case Ident(flag)      => FeatureFlag.NamedFeatureFlag(flag)
+      case _                => fail("Expected identifier")
+    }
+
+  def maybeFeatureFlag(): FeatureFlag =
+    nonterminal:
+      backtrack(featureFlag()).getOrElse(FeatureFlag.Default)
+
+  def externType(): Def =
+    nonterminal:
+      ExternType(`extern` ~> `type` ~> idDef(), maybeTypeParams())
+  def externInterface(): Def =
+    nonterminal:
+      ExternInterface(`extern` ~> `interface` ~> idDef(), maybeTypeParams())
+  def externResource(): Def =
+    nonterminal:
+      ExternResource(`extern` ~> `resource` ~> idDef(), blockTypeAnnotation())
+  def externInclude(): Def =
+    nonterminal:
+      `extern` ~> `include` ~> ExternInclude(maybeFeatureFlag(), path().stripPrefix("\"").stripSuffix("\""), None)
+
+  def externString(): Def =
+    nonterminal:
+      consume(`extern`)
+      val ff = maybeFeatureFlag()
+      next().kind match {
+        case Str(contents, _) => ExternInclude(ff, "", Some(contents))
+        case _ => fail("Expected string literal.")
+      }
+
+  def externFun(): Def =
+    nonterminal:
+      ((`extern` ~> maybeExternCapture()) ~ (`def` ~> idDef()) ~ params() ~ (returnAnnotation() <~ `=`)) match {
+        case capt ~ id ~ (tps, vps, bps) ~ ret =>
+          val bodies = manyWhile(externBody(), isExternBodyStart)
+          ExternDef(capt, id, tps, vps, bps, ret, bodies)
+      }
+
+  def externBody(): ExternBody =
+    nonterminal:
+      peek.kind match {
+        case _: Ident => peek(1).kind match {
+          case `{` => ExternBody.EffektExternBody(featureFlag(), `{` ~> stmts() <~ `}`)
+          case _ => ExternBody.StringExternBody(maybeFeatureFlag(), template())
+        }
+        case _ => ExternBody.StringExternBody(maybeFeatureFlag(), template())
+      }
+
+  private def isExternBodyStart: Boolean =
+    peek.kind match {
+      case Str(_, _) | Ident(_) | `{` => true
+      case _                          => false
+    }
+
+  def template(): Template[Term] =
+    nonterminal:
+      // TODO handle case where the body is not a string, e.g.
+      // Expected an extern definition, which can either be a single-line string (e.g., "x + y") or a multi-line string (e.g., """...""")
+      val first = string()
+      val (exprs, strs) = manyWhile((`${` ~> expr() <~ `}`, string()), `${`).unzip
+      Template(first :: strs, exprs)
+
+  def maybeExternCapture(): CaptureSet =
+    nonterminal:
+      if peek(`{`) || peek(`pure`) || isVariable then externCapture()
+      else CaptureSet(List(IdRef(List("effekt"), "io")))
+
+  def externCapture(): CaptureSet =
+    nonterminal:
+      if peek(`{`) then captureSet()
+      else if peek(`pure`) then `pure` ~> CaptureSet(Nil)
+      else CaptureSet(List(idRef()))
+
+  def path(): String =
+    nonterminal:
+      next().kind match {
+        case Str(s, false) => s
+        case _ => fail("Expected path as string literal.")
+      }
+
+  def string(): String =
+    nonterminal:
+      next().kind match {
+        case Str(s, _) => s
+        case _ => fail("Expected string literal.")
+      }
+
+
+  def maybeValueTypeAnnotation(): Option[ValueType] =
+    nonterminal:
+      if peek(`:`) then Some(valueTypeAnnotation()) else None
+
+  def maybeBlockTypeAnnotation(): Option[BlockType] =
+    nonterminal:
+      if peek(`:`) then Some(blockTypeAnnotation()) else None
+
+  def maybeReturnAnnotation(): Option[Effectful] =
+    nonterminal:
+      when(`:`) { Some(effectful()) } { None }
+
+  def returnAnnotation(): Effectful =
+    if peek(`:`) then  `:` ~> effectful()
+    else fail("Expected return type annotation")
+
+  def valueTypeAnnotation(): ValueType =
+    if peek(`:`) then  `:` ~> valueType()
+    else fail("Expected a type annotation")
+
+  def blockTypeAnnotation(): BlockType =
+    if peek(`:`) then  `:` ~> blockType()
+    else fail("Expected a type annotation")
+
+  def expr(): Term = peek.kind match {
+    case _ => matchExpr()
+  }
+
+  def ifExpr(): Term =
+    nonterminal:
+      If(`if` ~> parens { matchGuards() },
+        stmt(),
+        when(`else`) { stmt() } { Return(UnitLit()) })
+
+  def whileExpr(): Term =
+    nonterminal:
+      While(`while` ~> parens { matchGuards() },
+        stmt(),
+        when(`else`) { Some(stmt()) } { None })
+
+  def doExpr(): Term =
+    nonterminal:
+      (`do` ~> idRef()) ~ arguments() match {
+        case id ~ (targs, vargs, bargs) => Do(None, id, targs, vargs, bargs)
+      }
+
+  /*
+  <tryExpr> ::= try { <stmts> } <handler>+
+  <handler> ::= with (<idDef> :)? <implementation>
+  <implementation ::= <interfaceType> { <opClause>+ }
+  */
+  def tryExpr(): Term =
+    nonterminal:
+      `try` ~> stmt() ~ someWhile(handler(), `with`) match {
+        case s ~ hs => TryHandle(s, hs)
+      }
+
+  def regionExpr(): Term =
+    nonterminal:
+      Region(`region` ~> idDef(), stmt())
+
+  def boxExpr(): Term =
+    nonterminal:
+      val captures = `box` ~> backtrack(captureSet())
+      val expr = if (peek(`{`)) functionArg()
+        else if (peek(`new`)) newExpr()
+        else Var(idRef())
+      Box(captures, expr)
+
+
+  // TODO deprecate
+  def funExpr(): Term =
+    nonterminal:
+      `fun` ~> Box(None, BlockLiteral(Nil, valueParams(), Nil, braces { stmts() }))
+    // TODO positions
+
+  def unboxExpr(): Term =
+    nonterminal:
+      Unbox(`unbox` ~> expr())
+
+  def newExpr(): Term =
+    nonterminal:
+      New(`new` ~> implementation())
+
+  def handler(): Handler =
+    nonterminal:
+      `with` ~> backtrack(idDef() <~ `:`) ~ implementation() match {
+        case capabilityName ~ impl =>
+          val capability = capabilityName map { name => BlockParam(name, Some(impl.interface)): BlockParam }
+          Handler(capability, impl)
+      }
+
+  // This nonterminal uses limited backtracking: It parses the interface type multiple times.
+  def implementation(): Implementation =
+    nonterminal:
+      // Interface[...] {}
+      def emptyImplementation() = backtrack { Implementation(interfaceType(), `{` ~> Nil <~ `}`) }
+
+      // Interface[...] { def <NAME> = ... }
+      def interfaceImplementation() = backtrack {
+        val tpe = interfaceType()
+        consume(`{`)
+        if !peek(`def`) then fail("Expected at least one operation definition to implement this interface.")
+        tpe
+      } map { tpe =>
+        Implementation(tpe, manyWhile(opClause(), `def`)) <~ `}`
+      }
+
+      // Interface[...] { () => ... }
+      // Interface[...] { case ... => ... }
+      def operationImplementation() = idRef() ~ maybeTypeParams() ~ implicitResume ~ functionArg() match {
+        case (id ~ tps ~ k ~ BlockLiteral(_, vps, bps, body)) =>
+          val synthesizedId = IdRef(Nil, id.name).withPositionOf(id)
+          val interface = BlockTypeRef(id, Nil).withPositionOf(id): BlockTypeRef
+          val operation = OpClause(synthesizedId, tps, vps, bps, None, body, k).withRangeOf(id, body)
+          Implementation(interface, List(operation))
+      }
+
+      emptyImplementation() orElse interfaceImplementation() getOrElse operationImplementation()
+
+  def opClause(): OpClause =
+    nonterminal:
+      (`def` ~> idRef()) ~ paramsOpt() ~ maybeReturnAnnotation() ~ (`=` ~> stmt()) match {
+        case id ~ (tps, vps, bps) ~ ret ~ body =>
+          // TODO the implicitResume needs to have the correct position assigned (maybe move it up again...)
+          OpClause(id, tps, vps, bps, ret, body, implicitResume)
+      }
+
+  def implicitResume: IdDef =
+    nonterminal:
+      IdDef("resume")
+
+  def matchClause(): MatchClause =
+    nonterminal:
+      MatchClause(`case` ~> matchPattern(), manyWhile(`and` ~> matchGuard(), `and`), `=>` ~> stmts())
+
+  def matchGuards() =
+    nonterminal:
+      some(matchGuard, `and`)
+
+  def matchGuard(): MatchGuard =
+    nonterminal:
+      expr() ~ when(`is`) { Some(matchPattern()) } { None } match {
+        case e ~ Some(p) => MatchGuard.PatternGuard(e, p)
+        case e ~ None    => MatchGuard.BooleanGuard(e)
+      }
+
+  def matchPattern(): MatchPattern =
+    nonterminal:
+      peek.kind match {
+        case `__` => skip(); IgnorePattern()
+        case _ if isVariable  =>
+          idRef() match {
+            case id if peek(`(`) => TagPattern(id, many(matchPattern, `(`, `,`, `)`))
+            case IdRef(Nil, name) => AnyPattern(IdDef(name)) // TODO positions
+            case IdRef(_, name) => fail("Cannot use qualified names to bind a pattern variable")
+          }
+        case _ if isVariable =>
+          AnyPattern(idDef())
+        case _ if isLiteral => LiteralPattern(literal())
+        case `(` => some(matchPattern, `(`, `,`, `)`) match {
+          case p :: Nil => fail("Pattern matching on tuples requires more than one element")
+          case ps => TagPattern(IdRef(List("effekt"), s"Tuple${ps.size}"), ps)
+        }
+        case _ => fail("Expected pattern")
+      }
+
+  def matchExpr(): Term =
+    nonterminal:
+      var sc = assignExpr()
+      while (peek(`match`)) {
+         val clauses = `match` ~> braces { manyWhile(matchClause(), `case`) }
+         val default = when(`else`) { Some(stmt()) } { None }
+         sc = Match(sc, clauses, default)
+      }
+      sc
+
+  def assignExpr(): Term =
+    nonterminal:
+      orExpr() match {
+        case x @ Term.Var(id) => when(`=`) { Assign(id, expr()) } { x }
+        case other => other
+      }
+
+  def orExpr(): Term = infix(andExpr, `||`)
+  def andExpr(): Term = infix(eqExpr, `&&`)
+  def eqExpr(): Term = infix(relExpr, `===`, `!==`)
+  def relExpr(): Term = infix(addExpr, `<=`, `>=`, `<`, `>`)
+  def addExpr(): Term = infix(mulExpr, `++`, `+`, `-`)
+  def mulExpr(): Term = infix(callExpr, `*`, `/`)
+
+  inline def infix(nonTerminal: () => Term, ops: TokenKind*): Term =
+    nonterminal:
+      var left = nonTerminal()
+      while (ops.contains(peek.kind)) {
+         val op = next().kind
+         val right = nonTerminal()
+         left = binaryOp(left, op, right).withRangeOf(left, right)
+      }
+      left
+
+  // === AST Helpers ===
+
+  private def binaryOp(lhs: Term, op: TokenKind, rhs: Term): Term =
+    nonterminal:
+       if isThunkedOp(op) then
+         Call(IdTarget(IdRef(Nil, opName(op))), Nil, Nil, List(BlockLiteral(Nil, Nil, Nil, Return(lhs)), BlockLiteral(Nil, Nil, Nil, Return(rhs))))
+       else
+         Call(IdTarget(IdRef(Nil, opName(op))), Nil, List(lhs, rhs), Nil)
+
+  private def isThunkedOp(op: TokenKind): Boolean = op match {
+    case `||` | `&&` => true
+    case _           => false
+  }
+
+  private def opName(op: TokenKind): String = op match {
+    case `||` => "infixOr"
+    case `&&` => "infixAnd"
+    case `===` => "infixEq"
+    case `!==` => "infixNeq"
+    case `<` => "infixLt"
+    case `>` => "infixGt"
+    case `<=` => "infixLte"
+    case `>=` => "infixGte"
+    case `+` => "infixAdd"
+    case `-` => "infixSub"
+    case `*` => "infixMul"
+    case `/` => "infixDiv"
+    case `++` => "infixConcat"
+    case _ => sys.error(s"Internal compiler error: not a valid operator ${op}")
+  }
+
+  private def TupleTypeTree(tps: List[ValueType]): ValueType =
+    ValueTypeRef(IdRef(List("effekt"), s"Tuple${tps.size}"), tps)
+    // TODO positions!
+
+  /**
+   * This is a compound production for
+   *  - member selection <EXPR>.<NAME>
+   *  - method calls <EXPR>.<NAME>(...)
+   *  - function calls <EXPR>(...)
+   *
+   * This way expressions like `foo.bar.baz()(x).bam.boo()` are
+   * parsed with the correct left-associativity.
+   */
+  def callExpr(): Term = nonterminal {
+    var e = primExpr()
+
+    while (peek(`.`) || isArguments)
+      peek.kind match {
+        // member selection (or method call)
+        //   <EXPR>.<NAME>
+        // | <EXPR>.<NAME>( ... )
+        case `.` =>
+          consume(`.`)
+          val member = idRef()
+          // method call
+          if (isArguments) {
+            val (targs, vargs, bargs) = arguments()
+            e = Term.MethodCall(e, member, targs, vargs, bargs)
+          } else {
+            e = Term.MethodCall(e, member, Nil, Nil, Nil)
+          }
+
+        // function call
+        case _ if isArguments =>
+          val callee = e match {
+            case Term.Var(id) => IdTarget(id)
+            case other => ExprTarget(other)
+          }
+          val (targs, vargs, bargs) = arguments()
+          e = Term.Call(callee, targs, vargs, bargs)
+
+        // nothing to do
+        case _ => ()
+      }
+
+    e
+  }
+
+  // argument lists cannot follow a linebreak:
+  //   foo      ==    foo;
+  //   ()             ()
+  def isArguments: Boolean = lookbehind(1).kind != Newline && (peek(`(`) || peek(`[`) || peek(`{`))
+  def arguments(): (List[ValueType], List[Term], List[Term]) =
+    if (!isArguments) fail("Expected at least one argument section (types, values, or blocks)")
+    (maybeTypeArgs(), maybeValueArgs(), maybeBlockArgs())
+
+  def maybeTypeArgs(): List[ValueType] = if peek(`[`) then typeArgs() else Nil
+  def maybeValueArgs(): List[Term] = if peek(`(`) then valueArgs() else Nil
+  def maybeBlockArgs(): List[Term] = if peek(`{`) then blockArgs() else Nil
+
+  def typeArgs(): List[ValueType] =
+    nonterminal:
+      some(valueType, `[`, `,`, `]`)
+  def valueArgs(): List[Term] =
+    nonterminal:
+      many(expr, `(`, `,`, `)`)
+  def blockArgs(): List[Term] =
+    nonterminal:
+      someWhile(blockArg(), `{`)
+
+  /**
+   * Note: for this nonterminal, we need some backtracking.
+   */
+  def blockArg(): Term =
+    nonterminal:
+      backtrack { `{` ~> Var(idRef()) <~ `}` } getOrElse { functionArg() }
+
+  def functionArg(): BlockLiteral =
+    nonterminal:
+      braces {
+        peek.kind match {
+          // { case ... => ... }
+          case `case` => someWhile(matchClause(), `case`) match { case cs =>
+            // TODO positions should be improved here and fresh names should be generated for the scrutinee
+            // also mark the temp name as synthesized to prevent it from being listed in VSCode
+            val name = "__tmpRes"
+            BlockLiteral(
+              Nil,
+              List(ValueParam(IdDef(name), None)),
+              Nil,
+              Return(Match(Var(IdRef(Nil, name)), cs, None))) : BlockLiteral
+          }
+          case _ =>
+            // { (x: Int) => ... }
+            backtrack { lambdaParams() <~ `=>` } map {
+              case (tps, vps, bps) => BlockLiteral(tps, vps, bps, stmts()) : BlockLiteral
+            } getOrElse {
+              // { <STMTS> }
+              BlockLiteral(Nil, Nil, Nil, stmts()) : BlockLiteral
+            }
+        }
+      }
+
+  def primExpr(): Term = peek.kind match {
+    case `if`     => ifExpr()
+    case `while`  => whileExpr()
+    case `try`    => tryExpr()
+    case `region` => regionExpr()
+    case `box`    => boxExpr()
+    case `unbox`  => unboxExpr()
+    case `fun`    => funExpr()
+    case `new`    => newExpr()
+    case `do`                => doExpr()
+    case _ if isLiteral      => literal()
+    case _ if isVariable     => variable()
+    case _ if isHole         => hole()
+    case _ if isTupleOrGroup => tupleOrGroup()
+    case _ if isListLiteral  => listLiteral()
+    case _ => fail(s"Expected variables, literals, tuples, lists, holes or group")
+  }
+
+  def isListLiteral: Boolean = peek.kind match {
+    case `[` => true
+    case _ => false
+  }
+  def listLiteral(): Term =
+    nonterminal:
+      many(expr, `[`, `,`, `]`).foldRight(NilTree) { ConsTree }
+
+  private def NilTree: Term =
+    Call(IdTarget(IdRef(List(), "Nil")), Nil, Nil, Nil)
+
+  private def ConsTree(el: Term, rest: Term): Term =
+    Call(IdTarget(IdRef(List(), "Cons")), Nil, List(el, rest), Nil)
+
+  def isTupleOrGroup: Boolean = peek(`(`)
+  def tupleOrGroup(): Term =
+    some(expr, `(`, `,`, `)`) match {
+      case e :: Nil => e
+      case xs => Call(IdTarget(IdRef(List("effekt"), s"Tuple${xs.size}")), Nil, xs.toList, Nil)
+    }
+
+  def isHole: Boolean = peek(`<>`) || peek(`<{`)
+  def hole(): Term = peek.kind match {
+    case `<>` => `<>` ~> Hole(Return(UnitLit()))
+    case `<{` => `<{` ~> Hole(stmts()) <~ `}>`
+    case _ => fail("Expected hole")
+  }
+
+  def isLiteral: Boolean = peek.kind match {
+    case _: (Integer | Float | Str | Chr) => true
+    case `true` => true
+    case `false` => true
+    case _ => isUnitLiteral
+  }
+  def literal(): Literal =
+    nonterminal:
+      peek.kind match {
+        case Integer(v)         => skip(); IntLit(v)
+        case Float(v)           => skip(); DoubleLit(v)
+        case Str(s, multiline)  => skip(); StringLit(s)
+        case Chr(c)             => skip(); CharLit(c)
+        case `true`             => skip(); BooleanLit(true)
+        case `false`            => skip(); BooleanLit(false)
+        case t if isUnitLiteral => skip(); skip(); UnitLit()
+        case t => fail("Expected a literal")
+      }
+
+  // Will also recognize ( ) as unit if we do not emit space in the lexer...
+  private def isUnitLiteral: Boolean = peek(`(`) && peek(1, `)`)
+
+  def isVariable: Boolean = isIdRef
+  def variable(): Term =
+    nonterminal:
+      Var(idRef())
+
+  def isIdRef: Boolean = isIdent
+
+  def idRef(): IdRef =
+    nonterminal:
+      some(ident, PathSep) match {
+        case ids => IdRef(ids.init, ids.last)
+      }
+
+  def idDef(): IdDef =
+    nonterminal:
+      IdDef(ident())
+
+  def isIdent: Boolean = peek.kind match {
+    case Ident(id) => true
+    case _ => false
+  }
+  def ident(): String =
+    nonterminal:
+      next().kind match {
+        case Ident(id) => id
+        case _ => fail(s"Expected identifier")
+      }
+
+
+  /**
+   * Types
+   */
+
+  def valueType(): ValueType = valueType2(true)
+
+  /**
+   * Uses backtracking!
+   *
+   * This is not very efficient. To parse a value type, we first parse a block type,
+   * just to see that it either is no blocktype or it is not followed by an `at`
+   * and just "looked" like a block type...
+   *
+   * The parameter [[boxedAllowed]] controls whether on the right a dangling `at`
+   * can occur. This way we prevent parsing `() => S at {} at {}` and force users
+   * to manually parenthesize.
+   */
+  private def valueType2(boxedAllowed: Boolean): ValueType = nonterminal {
+    def boxedBlock = backtrack {
+      BoxedType(blockType2(false), `at` ~> captureSet())
+    }
+    if (boxedAllowed) { boxedBlock getOrElse atomicValueType() }
+    else atomicValueType()
+  }
+
+  def atomicValueType(): ValueType =
+    nonterminal:
+      peek.kind match {
+        case `(` => some(valueType, `(`, `,`, `)`) match {
+          case tpe :: Nil => tpe
+          case tpes => TupleTypeTree(tpes)
+        }
+        case _ => ValueTypeRef(idRef(), maybeTypeArgs())
+      }
+
+
+  /**
+   * Uses backtracking!
+   *
+   * TODO improve errors
+   *   i.e. fail("Expected either a function type (e.g., (A) => B / {E} or => B) or an interface type (e.g., State[T]).")
+   */
+  def blockType(): BlockType = blockType2(true)
+  private def blockType2(boxedAllowed: Boolean): BlockType =
+    nonterminal:
+
+      def simpleFunType = backtrack {
+        ValueTypeRef(idRef(), maybeTypeArgs()) <~ `=>`
+      } map { tpe =>
+        FunctionType(Nil, List(tpe), Nil, valueType2(boxedAllowed), maybeEffects())
+      }
+
+      def funType = backtrack {
+        maybeTypeParams() ~ maybeValueTypes() ~ (maybeBlockTypeParams() <~ `=>`) ~ valueType2(boxedAllowed) ~ maybeEffects() match {
+          case tparams ~ vparams ~ bparams ~ t ~ effs => FunctionType(tparams, vparams, bparams, t, effs)
+        }
+      }
+      def parenthesized = backtrack { parens { blockType() } }
+
+      def interface() =
+        val res = interfaceType()
+        if peek(`/`) then
+          fail("Effects not allowed here. Maybe you mean to use a function type `() => T / E`?")
+        else res
+
+      simpleFunType orElse funType orElse parenthesized getOrElse interface()
+
+  def interfaceType(): BlockTypeRef =
+    nonterminal:
+      BlockTypeRef(idRef(), maybeTypeArgs()): BlockTypeRef
+    // TODO error "Expected an interface type"
+
+  def maybeTypeParams(): List[Id] =
+    nonterminal:
+      if peek(`[`) then typeParams() else Nil
+
+  def typeParams(): List[Id] =
+    nonterminal:
+      some(idDef, `[`, `,`, `]`)
+
+  def maybeBlockTypeParams(): List[(Option[IdDef], BlockType)] =
+    nonterminal:
+      if peek(`{`) then blockTypeParams() else Nil
+
+  def blockTypeParams(): List[(Option[IdDef], BlockType)] =
+    nonterminal:
+      someWhile(blockTypeParam(), `{`)
+
+  def blockTypeParam(): (Option[IdDef], BlockType) =
+    nonterminal:
+      braces { (backtrack { idDef() <~ `:` }, blockType()) }
+
+  def lambdaParams(): (List[Id], List[ValueParam], List[BlockParam]) =
+    nonterminal:
+      if isVariable then (Nil, List(ValueParam(idDef(), None)), Nil)  else paramsOpt()
+
+  def params(): (List[Id], List[ValueParam], List[BlockParam]) =
+    nonterminal:
+      maybeTypeParams() ~ maybeValueParams() ~ maybeBlockParams() match {
+        case tps ~ vps ~ bps => (tps, vps, bps)
+      }
+
+  def paramsOpt(): (List[Id], List[ValueParam], List[BlockParam]) =
+    nonterminal:
+      maybeTypeParams() ~ maybeValueParamsOpt() ~ maybeBlockParamsOpt() match {
+        case (tps ~ vps ~ bps) =>
+          // fail("Expected a parameter list (multiple value parameters or one block parameter; only type annotations of value parameters can be currently omitted)")
+          (tps, vps, bps)
+      }
+
+  def maybeValueParamsOpt(): List[ValueParam] =
+    nonterminal:
+      if peek(`(`) then valueParamsOpt() else Nil
+
+  def valueParamsOpt(): List[ValueParam] =
+    nonterminal:
+      many(valueParamOpt, `(`, `,`, `)`)
+
+  def maybeValueParams(): List[ValueParam] =
+    nonterminal:
+      if peek(`(`) then valueParams() else Nil
+
+  def valueParams(): List[ValueParam] =
+    nonterminal:
+      many(valueParam, `(`, `,`, `)`)
+
+  def valueParam(): ValueParam =
+    nonterminal:
+      ValueParam(idDef(), Some(valueTypeAnnotation()))
+
+  def valueParamOpt(): ValueParam =
+    nonterminal:
+      ValueParam(idDef(), maybeValueTypeAnnotation())
+
+  def maybeBlockParams(): List[BlockParam] =
+    nonterminal:
+      manyWhile(`{` ~> blockParam() <~ `}`, `{`)
+
+  def blockParams(): List[BlockParam] =
+    nonterminal:
+      someWhile(`{` ~> blockParam() <~ `}`, `{`)
+
+  def maybeBlockParamsOpt(): List[BlockParam] =
+    nonterminal:
+      manyWhile(`{` ~> blockParamOpt() <~ `}`, `{`)
+
+  def blockParamsOpt(): List[BlockParam] =
+    nonterminal:
+      someWhile(`{` ~> blockParamOpt() <~ `}`, `{`)
+
+  def blockParam(): BlockParam =
+    nonterminal:
+      BlockParam(idDef(), Some(blockTypeAnnotation()))
+
+  def blockParamOpt(): BlockParam =
+    nonterminal:
+      BlockParam(idDef(), when(`:`)(Some(blockType()))(None))
+
+
+  def maybeValueTypes(): List[ValueType] =
+    nonterminal:
+      if peek(`(`) then valueTypes() else Nil
+
+  def valueTypes(): List[ValueType] =
+    nonterminal:
+      many(valueType, `(`, `,`, `)`)
+
+  def captureSet(): CaptureSet =
+    nonterminal:
+      CaptureSet(many(idRef, `{`, `,` , `}`))
+
+  def effectful(): Effectful =
+    nonterminal:
+      Effectful(valueType(), maybeEffects())
+
+  def maybeEffects(): Effects =
+    nonterminal:
+      when(`/`) { effects() } { Effects.Pure }
+
+  // TODO error "Expected an effect set"
+  def effects(): Effects =
+    nonterminal:
+      if peek(`{`) then Effects(many(interfaceType, `{`, `,`, `}`))
+      else Effects(interfaceType())
+
+
+  // Generic utility functions
+  // -------------------------
+  // ... for writing parsers.
+
+  /**
+   * Aborts parsing with the given message
+   */
+  def fail(message: String): Nothing = throw Fail(message, position)
+
+  /**
+   * Guards `thn` by token `t` and consumes the token itself, if present.
+   */
+  inline def when[T](t: TokenKind)(inline thn: => T)(inline els: => T): T =
+    if peek(t) then { consume(t); thn } else els
+
+  inline def backtrack[T](inline p: => T): Option[T] =
+    val before = position
+    try { Some(p) } catch {
+      case Fail(_, _) => position = before; None
+    }
+
+  /**
+   * Tiny combinator DSL to sequence parsers
+   */
+  case class ~[+T, +U](_1: T, _2: U) {
+    override def toString = s"(${_1}~${_2})"
+  }
+
+  extension [A](self: A) {
+    @targetName("seq")
+    inline def ~[B](other: B): (A ~ B) = new ~(self, other)
+
+    @targetName("seqLeftToken")
+    inline def <~(t: TokenKind): A = { consume(t); self }
+
+    @targetName("seqLeftUnit")
+    inline def <~(t: Unit): A = { self }
+
+    inline def |(other: A): A = { backtrack(self).getOrElse(other) }
+  }
+
+  extension (self: TokenKind) {
+    @targetName("seqRightToken")
+    inline def ~>[R](other: => R): R = { consume(self); other }
+  }
+
+  extension (self: Unit) {
+    @targetName("seqRightUnit")
+    inline def ~>[R](other: => R): R = { other }
+  }
+
+  /**
+   * Repeats [[p]], separated by [[sep]] enclosed by [[before]] and [[after]]
+   */
+  inline def some[T](p: () => T, before: TokenKind, sep: TokenKind, after: TokenKind): List[T] =
+    consume(before)
+    val res = some(p, sep)
+    consume(after)
+    res
+
+  inline def some[T](p: () => T, sep: TokenKind): List[T] =
+    val components: ListBuffer[T] = ListBuffer.empty
+    components += p()
+    while (peek(sep)) {
+      consume(sep)
+      components += p()
+    }
+    components.toList
+
+  inline def someWhile[T](p: => T, lookahead: TokenKind): List[T] =
+    someWhile(p, peek(lookahead))
+
+  inline def someWhile[T](p: => T, predicate: => Boolean): List[T] =
+    val components: ListBuffer[T] = ListBuffer.empty
+    components += p
+    while (predicate) {
+      components += p
+    }
+    components.toList
+
+  inline def manyWhile[T](p: => T, lookahead: TokenKind): List[T] =
+    manyWhile(p, peek(lookahead))
+
+  inline def manyWhile[T](p: => T, predicate: => Boolean): List[T] =
+    val components: ListBuffer[T] = ListBuffer.empty
+    while (predicate) {
+      components += p
+    }
+    components.toList
+
+  inline def parens[T](p: => T): T =
+    consume(`(`)
+    val res = p
+    consume(`)`)
+    res
+
+  inline def braces[T](p: => T): T =
+    consume(`{`)
+    val res = p
+    consume(`}`)
+    res
+
+  inline def many[T](p: () => T, before: TokenKind, sep: TokenKind, after: TokenKind): List[T] =
+    consume(before)
+    if (peek(after)) {
+      consume(after)
+      Nil
+    } else {
+      val components: ListBuffer[T] = ListBuffer.empty
+      components += p()
+      while (peek(sep)) {
+        consume(sep)
+        components += p()
+      }
+      consume(after)
+      components.toList
+    }
+
+
+  // Positions
+
+  inline def nonterminal[T](inline p: => T): T = {
+    val startToken = peek
+    val start = startToken.start
+    val res = p
+    val endToken = previous
+    val end = endToken.end + 1 // since positions by lexer are inclusive, but kiama is exclusive
+
+    //    val sc: Any = res
+    //    if sc.isInstanceOf[Implementation] then sc match {
+    //      case Implementation(_, List(op)) =>
+    //        println(op)
+    //        println(positions.getStart(op))
+    //        println(positions.getFinish(op))
+    //
+    //        //        println(s"start: ${startToken.kind} (${source.offsetToPosition(start)})")
+    //        //        println(s"end: ${endToken} (${source.offsetToPosition(end)})")
+    //        //        println(s"peek: ${peek}")
+    //
+    //      case _ => ()
+    //    }
+
+    positions.setStart(res, source.offsetToPosition(start))
+    positions.setFinish(res, source.offsetToPosition(end))
+
+    res
+  }
+
+  extension [T](self: T) {
+    inline def withPositionOf(other: Any): self.type = { positions.dupPos(other, self); self }
+    inline def withRangeOf(first: Any, last: Any): self.type = { positions.dupRangePos(first, last, self); self }
+
+    // Why did we need those?
+    private def dupIfEmpty(from: Any, to: Any): Unit =
+      if (positions.getStart(to).isEmpty) { positions.dupPos(from, to) }
+
+    private def dupAll(from: Any, to: Any): Unit = to match {
+      case t: Tree =>
+        dupIfEmpty(from, t)
+        t.productIterator.foreach { dupAll(from, _) }
+      case t: Iterable[t] => t.foreach { dupAll(from, _) }
+      case _ => ()
+    }
+  }
+}
diff --git a/effekt/shared/src/main/scala/effekt/core/Parser.scala b/effekt/shared/src/main/scala/effekt/core/Parser.scala
index 2b2c76dc..108de77c 100644
--- a/effekt/shared/src/main/scala/effekt/core/Parser.scala
+++ b/effekt/shared/src/main/scala/effekt/core/Parser.scala
@@ -4,7 +4,7 @@ package core
 import effekt.core.Param.ValueParam
 import effekt.source.{FeatureFlag, NoSource}
 import effekt.util.messages.{ DebugMessaging, ErrorReporter, ParseError }
-import kiama.parsing.{ Failure, Input, NoSuccess, ParseResult, Parsers, Success }
+import kiama.parsing.{ Failure, Input, NoSuccess, ParseResult, Success }
 import kiama.util.{ Position, Positions, Range, Source, StringSource }
 
 import scala.util.matching.Regex
@@ -23,7 +23,6 @@ class CoreParsers(positions: Positions, names: Names) extends EffektLexers(posit
     parseAll(program, source) match {
       case Success(ast, _) =>
         Some(ast)
-
       case res: NoSuccess =>
         val input = res.next
         val range = Range(input.position, input.nextPosition)
diff --git a/effekt/shared/src/main/scala/effekt/generator/chez/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/chez/Transformer.scala
index 3eabf5c8..72218105 100644
--- a/effekt/shared/src/main/scala/effekt/generator/chez/Transformer.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/chez/Transformer.scala
@@ -43,6 +43,8 @@ object TransformerCallCC extends Transformer {
 
 trait Transformer {
 
+  val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r")
+
   def run(expr: chez.Expr): chez.Expr
   def pure(expr: chez.Expr): chez.Expr
   def bind(binding: chez.Expr, param: ChezName, body: chez.Block): chez.Expr
@@ -216,7 +218,7 @@ trait Transformer {
   def toChez(expr: Expr): chez.Expr = expr match {
     case Literal((), _)         => chez.RawValue("#f")
 
-    case Literal(s: String, _)  => ChezString(chez.adaptEscapes(s))
+    case Literal(s: String, _)  => ChezString(adaptEscapes(escape(s)))
     case Literal(b: Boolean, _) => if (b) chez.RawValue("#t") else chez.RawValue("#f")
     case l: Literal             => chez.RawValue(l.value.toString)
     case ValueVar(id, _)        => chez.Variable(nameRef(id))
@@ -254,4 +256,12 @@ trait Transformer {
 
     List(getter, setter)
   }
+
+  def escape(scalaString: String): String =
+    scalaString.foldLeft(StringBuilder()) { (acc, c) =>
+      escapeSeqs.get(c) match {
+        case Some(s) => acc ++= s
+        case None => acc += c
+      }
+    }.toString()
 }
diff --git a/effekt/shared/src/main/scala/effekt/generator/chez/TransformerLift.scala b/effekt/shared/src/main/scala/effekt/generator/chez/TransformerLift.scala
index 0129a442..ec03429c 100644
--- a/effekt/shared/src/main/scala/effekt/generator/chez/TransformerLift.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/chez/TransformerLift.scala
@@ -21,6 +21,8 @@ import scala.language.implicitConversions
  */
 object TransformerLift {
 
+  val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r")
+
   def runMain(main: ChezName): chez.Expr =
     val monomorphized = false
     if (monomorphized) chez.Call(chez.Call(main), List(CPS.id))
@@ -294,7 +296,7 @@ object TransformerLift {
 
   def toChez(expr: Expr): chez.Expr = expr match {
     case Literal((), _) => chez.RawValue("#f")
-    case Literal(s: String, _) => ChezString(chez.adaptEscapes(s))
+    case Literal(s: String, _) => ChezString(chez.adaptEscapes(escape(s)))
     case Literal(b: Boolean, _) => if (b) chez.RawValue("#t") else chez.RawValue("#f")
     case l: Literal => chez.RawValue(l.value.toString)
     case ValueVar(id, _)  => chez.Variable(nameRef(id))
@@ -421,4 +423,11 @@ object TransformerLift {
     List(getter, setter)
   }
 
+  def escape(scalaString: String): String =
+    scalaString.foldLeft(StringBuilder()) { (acc, c) =>
+      escapeSeqs.get(c) match {
+        case Some(s) => acc ++= s
+        case None => acc += c
+      }
+    }.toString()
 }
diff --git a/effekt/shared/src/main/scala/effekt/generator/js/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/js/Transformer.scala
index 3f016f0f..52803239 100644
--- a/effekt/shared/src/main/scala/effekt/generator/js/Transformer.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/js/Transformer.scala
@@ -20,6 +20,8 @@ trait Transformer {
 
   val jsFeatureFlags: List[String] = List("js")
 
+  val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r")
+
   def run(body: js.Expr): js.Stmt
 
   def shouldExport(id: Id)(using D: DeclarationContext): Boolean = true
@@ -124,6 +126,14 @@ trait Transformer {
   def freshName(s: String): JSName =
     JSName(s + Symbol.fresh.next())
 
+  def escape(scalaString: String): String =
+    scalaString.foldLeft(StringBuilder()) { (acc, c) =>
+      escapeSeqs.get(c) match {
+        case Some(s) => acc ++= s
+        case None => acc += c
+      }
+    }.toString()
+
 
   // Separate Compilation (Website)
   // ------------------------------
diff --git a/effekt/shared/src/main/scala/effekt/generator/js/TransformerDirect.scala b/effekt/shared/src/main/scala/effekt/generator/js/TransformerDirect.scala
index 38b61bb0..77cbaf8e 100644
--- a/effekt/shared/src/main/scala/effekt/generator/js/TransformerDirect.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/js/TransformerDirect.scala
@@ -126,7 +126,7 @@ object TransformerDirect extends Transformer {
    */
   def toJS(expr: core.Expr)(using TransformerContext): js.Expr = expr match {
     case Literal((), _) => $effekt.field("unit")
-    case Literal(s: String, _) => JsString(s)
+    case Literal(s: String, _) => JsString(escape(s))
     case literal: Literal => js.RawExpr(literal.value.toString)
     case ValueVar(id, tpe) => nameRef(id)
     case DirectApp(b, targs, vargs, bargs) => js.Call(toJS(b), vargs.map(toJS) ++ bargs.map(toJS))
diff --git a/effekt/shared/src/main/scala/effekt/generator/js/TransformerMonadic.scala b/effekt/shared/src/main/scala/effekt/generator/js/TransformerMonadic.scala
index 82eb2e0c..57b43441 100644
--- a/effekt/shared/src/main/scala/effekt/generator/js/TransformerMonadic.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/js/TransformerMonadic.scala
@@ -176,7 +176,7 @@ trait TransformerMonadic extends Transformer {
 
   def toJS(expr: core.Expr)(using D: DeclarationContext, C: Context): js.Expr = expr match {
     case Literal((), _) => $effekt.field("unit")
-    case Literal(s: String, _) => JsString(s)
+    case Literal(s: String, _) => JsString(escape(s))
     case literal: Literal => js.RawExpr(literal.value.toString)
     case ValueVar(id, tpe) => nameRef(id)
 
diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala
index daac1042..314d8262 100644
--- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala
@@ -13,6 +13,8 @@ object Transformer {
 
   val llvmFeatureFlags: List[String] = List("llvm")
 
+  val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r")
+
   def transform(program: machine.Program)(using ErrorReporter): List[Definition] = program match {
     case machine.Program(declarations, statement) =>
       given MC: ModuleContext = ModuleContext();
@@ -768,4 +770,12 @@ object Transformer {
 
   def setStackPointer(stackPointer: Operand)(using C: BlockContext) =
     C.stackPointer = stackPointer;
+
+  def escape(scalaString: String): String =
+    scalaString.foldLeft(StringBuilder()) { (acc, c) =>
+      escapeSeqs.get(c) match {
+        case Some(s) => acc ++= s
+        case None => acc += c
+      }
+    }.toString()
 }
diff --git a/effekt/shared/src/main/scala/effekt/generator/ml/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/ml/Transformer.scala
index f6d425ed..81782271 100644
--- a/effekt/shared/src/main/scala/effekt/generator/ml/Transformer.scala
+++ b/effekt/shared/src/main/scala/effekt/generator/ml/Transformer.scala
@@ -17,6 +17,16 @@ import scala.collection.mutable
 
 object Transformer {
 
+  val escapeSeqs: Map[Char, String] = Map(
+    '\'' -> raw"'",
+    '\"' -> raw"\"",
+    '\\' -> raw"\\",
+    '\n' -> raw"\n",
+    '\t' -> raw"\t",
+    '\r' -> raw"\r")
+
+
+
   def runMain(main: MLName): ml.Expr = CPS.runMain(main)
 
   def compilationUnit(mainSymbol: Symbol, core: ModuleDecl)(using C: Context): ml.Toplevel = {
@@ -439,7 +449,7 @@ object Transformer {
         case v: Float => numberString(v)
         case v: Double => numberString(v)
         case _: Unit => Consts.unitVal
-        case v: String => MLString(v)
+        case v: String => MLString(escape(v))
         case v: Boolean => if (v) Consts.trueVal else Consts.falseVal
         case _ => ml.RawValue(l.value.toString)
       }
@@ -619,4 +629,16 @@ object Transformer {
     case one :: Nil => Some(one)
     case exps => Some(ml.Expr.Tuple(exps))
   }
+
+  def escape(scalaString: String): String = {
+    scalaString.foldLeft(new StringBuilder) {
+      case (acc, c) if escapeSeqs.isDefinedAt(c) =>
+        acc ++= escapeSeqs(c)
+      case (acc, c) if (c.isControl || c < ' ' || c > '~') =>
+        acc ++= f"\\u${c.toInt}%04x"
+      case (acc, c) =>
+        acc += c
+    }.toString()
+  }
+
 }
diff --git a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala
index 6a8b9a36..45636d3d 100644
--- a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala
+++ b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala
@@ -224,18 +224,6 @@ object Transformer {
           Invoke(Variable(transform(id), transform(tpe)), opTag, values)
         }
 
-      case lifted.App(lifted.Member(lifted.Unbox(lifted.ValueVar(id, lifted.ValueType.Boxed(tpe))), op, annotatedTpe), targs, args) =>
-        if(targs.exists(requiresBoxing)){ ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") }
-        val opTag = {
-          tpe match
-            case lifted.BlockType.Interface(ifceId, _) =>
-              DeclarationContext.getPropertyTag(op)
-            case _ => ErrorReporter.abort(s"Unsupported receiver type $tpe")
-        }
-        transform(args).run { values =>
-          Invoke(Variable(transform(id), transform(tpe)), opTag, values)
-        }
-
       case lifted.If(cond, thenStmt, elseStmt) =>
         transform(cond).run { value =>
           Switch(value, List(0 -> Clause(List(), transform(elseStmt)), 1 -> Clause(List(), transform(thenStmt))), None)
@@ -402,14 +390,9 @@ object Transformer {
         New(variable, List(Clause(parameters, transform(body))), k(variable))
       }
 
-    case lifted.New(impl) =>
-      val variable = Variable(freshName("g"), Negative())
-      Binding { k =>
-        New(variable, transform(impl), k(variable))
-      }
-
     case lifted.Member(b, field, annotatedTpe) => ???
     case lifted.Unbox(e) => ???
+    case lifted.New(impl) => ???
   }
 
   def transform(expr: lifted.Expr)(using BlocksParamsContext, DeclarationContext, ErrorReporter): Binding[Variable] = expr match {
@@ -504,15 +487,14 @@ object Transformer {
       case arg :: args => transform(arg).flatMap { value => transform(args).flatMap { values => pure(value :: values) } }
     }
 
-  def transform(impl: lifted.Implementation)(using BlocksParamsContext, DeclarationContext, ErrorReporter): List[Clause] =
-    impl.operations.sortBy {
+  def transform(handler: lifted.Implementation)(using BlocksParamsContext, DeclarationContext, ErrorReporter): List[Clause] =
+    handler.operations.sortBy {
       case lifted.Operation(operationName, _) =>
-        DeclarationContext.getInterface(impl.interface.name).properties.indexWhere(_.id == operationName)
+        DeclarationContext.getInterface(handler.interface.name).properties.indexWhere(_.id == operationName)
     }.map(transform)
 
   def transform(op: lifted.Operation)(using BlocksParamsContext, DeclarationContext, ErrorReporter): Clause = op match {
     case lifted.Operation(name, lifted.BlockLit(tparams, params, body)) =>
-      // TODO note block parameters
       Clause(params.map(transform), transform(body))
   }
 
diff --git a/effekt/shared/src/main/scala/effekt/util/AnsiHighlight.scala b/effekt/shared/src/main/scala/effekt/util/AnsiHighlight.scala
index 5f1e7a15..4dc30798 100644
--- a/effekt/shared/src/main/scala/effekt/util/AnsiHighlight.scala
+++ b/effekt/shared/src/main/scala/effekt/util/AnsiHighlight.scala
@@ -7,7 +7,7 @@ import scala.util.matching._
 
 object AnsiHighlight {
 
-  object parser extends EffektParsers(new Positions)
+  object parser extends EffektLexers(new Positions)
 
   case class Token(r: Regex, highlight: String => String = identity) {
     def apply(in: String): Option[(String, String)] =
diff --git a/examples/benchmarks/nqueens.effekt b/examples/benchmarks/nqueens.effekt
index 08a6f922..5db3c5eb 100644
--- a/examples/benchmarks/nqueens.effekt
+++ b/examples/benchmarks/nqueens.effekt
@@ -31,7 +31,7 @@ def findOneSolution(size: Int, queen: Int) : Solution / Search = {
   if (queen == 0) {
     Nil()
   } else {
-    val sol = findOneSolution(size, queen-1)
+    val sol = findOneSolution(size, queen - 1)
     val next = do pick(size)
     if (safe(next, 1, sol)) {
       Cons(next, sol)
diff --git a/examples/pos/doubles.check b/examples/ml/doubles.check
similarity index 87%
rename from examples/pos/doubles.check
rename to examples/ml/doubles.check
index d0ec34ae..e4eaa5bf 100644
--- a/examples/pos/doubles.check
+++ b/examples/ml/doubles.check
@@ -1,5 +1,4 @@
 4.1
-1
 2
 2.1
 1
diff --git a/examples/pos/doubles.effekt b/examples/ml/doubles.effekt
similarity index 86%
rename from examples/pos/doubles.effekt
rename to examples/ml/doubles.effekt
index 5ecd1330..be2762b1 100644
--- a/examples/pos/doubles.effekt
+++ b/examples/ml/doubles.effekt
@@ -1,7 +1,6 @@
 def main() = {
     println(1.2 + 2.0 * 3.0 / 4.0 + 1.4);
     println(toInt(1.5));
-    println(round(1.5));
     println(toDouble(2)+ 0.1);
     println(floor(1.7));
     println(ceil(2.1));
diff --git a/examples/ml/raytracer.check b/examples/ml/raytracer.check
index e2431864..1c6bb9b2 100644
--- a/examples/ml/raytracer.check
+++ b/examples/ml/raytracer.check
@@ -474,13 +474,13 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-81, 187, 126
-80, 185, 125
-77, 178, 120
-73, 168, 113
-68, 157, 105
-62, 143, 96
-55, 126, 85
+82, 188, 127
+81, 186, 125
+78, 179, 120
+73, 169, 114
+68, 157, 106
+62, 143, 97
+55, 127, 85
 45, 104, 70
 255, 255, 255
 255, 255, 255
@@ -535,20 +535,20 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-98, 227, 153
-99, 228, 153
-97, 223, 150
-94, 216, 146
-90, 208, 140
-86, 199, 134
-82, 188, 127
-77, 177, 119
-71, 165, 111
-65, 151, 102
-59, 136, 91
-51, 119, 80
-43, 99, 67
-32, 73, 49
+99, 227, 153
+99, 228, 154
+97, 224, 151
+94, 217, 146
+91, 209, 141
+87, 199, 134
+82, 189, 127
+77, 178, 120
+72, 165, 111
+66, 151, 102
+59, 136, 92
+52, 120, 81
+43, 100, 67
+32, 74, 50
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -597,24 +597,24 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
+109, 251, 169
+110, 253, 170
 108, 250, 168
-109, 252, 170
-108, 249, 168
 106, 244, 164
-103, 237, 159
-99, 228, 154
-95, 220, 148
-91, 210, 141
-86, 199, 134
-81, 188, 127
-76, 176, 119
-71, 163, 110
+103, 237, 160
+100, 229, 154
+96, 220, 148
+91, 210, 142
+87, 200, 135
+82, 189, 127
+77, 177, 119
+71, 164, 110
 65, 150, 101
-58, 135, 91
-51, 118, 80
-43, 100, 67
+59, 135, 91
+52, 119, 80
+44, 101, 68
 34, 79, 53
-21, 50, 34
+22, 51, 34
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -659,28 +659,28 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-111, 257, 173
-117, 269, 181
-117, 269, 181
-115, 265, 178
-112, 259, 175
-109, 252, 170
-106, 245, 165
-102, 236, 159
-98, 227, 153
-94, 217, 146
-89, 206, 139
-85, 195, 131
+112, 257, 173
+117, 270, 182
+117, 269, 182
+115, 265, 179
+113, 260, 175
+110, 253, 170
+107, 245, 165
+103, 236, 159
+99, 227, 153
+95, 217, 146
+90, 207, 139
+85, 196, 132
 80, 184, 124
-74, 171, 115
-68, 158, 106
-62, 144, 97
-56, 129, 87
+75, 172, 116
+69, 159, 107
+63, 145, 98
+56, 130, 87
 49, 113, 76
 41, 95, 64
-32, 74, 50
-21, 49, 33
-17, 40, 27
+33, 75, 50
+22, 50, 33
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -722,30 +722,30 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-120, 276, 186
+120, 277, 187
 123, 283, 191
-122, 282, 190
-121, 278, 187
-118, 272, 183
-115, 266, 179
-112, 258, 174
-108, 250, 168
-105, 241, 162
-100, 232, 156
-96, 222, 149
-92, 211, 142
-87, 200, 135
-82, 189, 127
-77, 177, 119
-71, 164, 110
-65, 151, 101
-59, 137, 92
-52, 121, 82
-45, 105, 70
-37, 87, 58
-28, 66, 44
+123, 282, 190
+121, 278, 188
+119, 273, 184
+116, 266, 179
+112, 259, 174
+109, 250, 169
+105, 242, 163
+101, 232, 156
+97, 222, 150
+92, 212, 143
+87, 201, 135
+82, 189, 128
+77, 177, 120
+72, 165, 111
+66, 151, 102
+60, 137, 92
+53, 122, 82
+46, 105, 71
+38, 87, 59
+29, 67, 45
 18, 42, 28
-17, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -785,32 +785,32 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-124, 287, 193
-127, 293, 198
-127, 292, 197
-125, 289, 194
-123, 283, 191
-120, 277, 187
-117, 270, 182
-114, 262, 176
-110, 253, 171
-106, 245, 165
-102, 235, 158
-98, 225, 152
-93, 215, 145
-88, 204, 137
-83, 193, 130
-78, 181, 122
-73, 168, 113
-67, 155, 105
-61, 142, 95
-55, 127, 85
-48, 112, 75
+125, 287, 194
+128, 294, 198
+127, 293, 197
+126, 289, 195
+123, 284, 191
+121, 278, 187
+118, 270, 182
+114, 262, 177
+110, 254, 171
+107, 245, 165
+102, 236, 159
+98, 226, 152
+94, 215, 145
+89, 204, 138
+84, 193, 130
+79, 181, 122
+73, 169, 114
+68, 156, 105
+62, 142, 96
+55, 128, 86
+49, 112, 76
 41, 95, 64
-33, 76, 51
-24, 55, 37
-17, 40, 27
-17, 40, 27
+33, 77, 52
+24, 56, 38
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -848,34 +848,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-127, 293, 197
-131, 301, 203
-131, 301, 203
-129, 298, 201
-127, 293, 197
-124, 287, 193
-122, 280, 189
-118, 273, 184
-115, 264, 178
-111, 256, 172
-107, 247, 166
-103, 237, 160
-99, 227, 153
-94, 217, 146
-89, 206, 139
-85, 195, 131
+127, 293, 198
+131, 302, 203
+131, 302, 203
+130, 298, 201
+128, 293, 198
+125, 287, 194
+122, 281, 189
+119, 273, 184
+115, 265, 179
+111, 256, 173
+108, 247, 167
+103, 238, 160
+99, 228, 154
+95, 218, 147
+90, 207, 139
+85, 196, 132
 80, 184, 124
-74, 171, 115
+75, 172, 116
 69, 159, 107
-63, 145, 98
-57, 131, 88
-50, 116, 78
-43, 100, 67
+63, 146, 98
+57, 132, 89
+51, 117, 79
+44, 101, 68
 36, 83, 56
-27, 64, 43
-18, 42, 28
-17, 40, 27
-17, 40, 27
+28, 64, 43
+19, 43, 29
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -911,36 +911,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-126, 289, 195
-133, 307, 207
-134, 308, 207
+126, 290, 195
+134, 307, 207
+134, 309, 208
 133, 306, 206
-131, 301, 203
-128, 296, 199
-125, 289, 195
-122, 282, 190
-119, 274, 185
-115, 266, 179
-112, 257, 173
-108, 248, 167
+131, 302, 203
+129, 296, 200
+126, 290, 195
+123, 282, 190
+119, 275, 185
+116, 267, 180
+112, 258, 174
+108, 249, 168
 104, 239, 161
-99, 229, 154
-95, 219, 147
-90, 208, 140
-85, 197, 133
-80, 185, 125
-75, 173, 117
-70, 161, 108
-64, 148, 99
-58, 134, 90
-52, 120, 80
-45, 104, 70
+100, 229, 155
+95, 219, 148
+91, 208, 140
+86, 197, 133
+81, 186, 125
+76, 174, 117
+70, 161, 109
+64, 148, 100
+59, 135, 91
+52, 120, 81
+46, 105, 71
 38, 88, 59
-30, 70, 47
-21, 50, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
+31, 70, 47
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -961,7 +961,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-111, 111, 111
+112, 112, 112
 33, 33, 33
 255, 255, 255
 255, 255, 255
@@ -975,36 +975,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-134, 310, 208
-136, 313, 211
-135, 312, 210
-134, 308, 208
-132, 303, 204
-129, 297, 200
-126, 290, 195
+135, 310, 209
+136, 314, 211
+136, 312, 210
+134, 309, 208
+132, 304, 205
+129, 298, 201
+126, 291, 196
 123, 283, 191
-119, 275, 185
+120, 276, 186
 116, 267, 180
-112, 258, 174
+112, 259, 174
 108, 249, 168
-104, 239, 161
+104, 240, 162
 100, 230, 155
-95, 219, 148
-90, 209, 141
-86, 198, 133
-81, 186, 125
-76, 175, 117
-70, 162, 109
-65, 149, 101
-59, 136, 91
+96, 220, 148
+91, 209, 141
+86, 198, 134
+81, 187, 126
+76, 175, 118
+71, 163, 110
+65, 150, 101
+59, 136, 92
 53, 122, 82
-46, 107, 72
-39, 91, 61
+47, 107, 72
+40, 91, 62
 32, 74, 50
 24, 55, 37
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1025,8 +1025,8 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-90, 90, 90
-11, 11, 11
+91, 91, 91
+12, 12, 12
 0, 0, 0
 255, 255, 255
 255, 255, 255
@@ -1038,38 +1038,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-133, 307, 207
-137, 316, 213
-137, 317, 213
-136, 314, 211
-134, 310, 208
-132, 304, 205
-129, 298, 200
-126, 291, 196
-123, 283, 191
-119, 275, 185
+134, 307, 207
+138, 317, 213
+138, 317, 214
+137, 314, 212
+135, 310, 209
+132, 305, 205
+130, 298, 201
+127, 291, 196
+123, 284, 191
+120, 276, 186
 116, 267, 180
-112, 258, 174
+112, 259, 174
 108, 249, 168
-104, 239, 161
+104, 240, 162
 100, 230, 155
-95, 219, 148
+96, 220, 148
 91, 209, 141
-86, 198, 133
+86, 199, 134
 81, 187, 126
-76, 175, 118
+76, 176, 118
 71, 163, 110
-65, 150, 101
-59, 137, 92
-53, 123, 83
+66, 151, 102
+60, 138, 93
+54, 124, 83
 47, 109, 73
-40, 93, 63
-33, 76, 51
-25, 58, 39
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 63
+33, 77, 52
+26, 59, 40
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1089,7 +1089,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-29, 29, 29
+30, 30, 30
 0, 0, 0
 255, 255, 255
 255, 255, 255
@@ -1102,38 +1102,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-137, 316, 213
+138, 316, 213
+139, 320, 216
 139, 319, 215
-138, 318, 214
 137, 315, 212
-134, 310, 209
-132, 304, 205
-129, 298, 200
-126, 290, 196
-123, 283, 190
-119, 275, 185
-116, 266, 179
-112, 258, 173
-108, 248, 167
+135, 310, 209
+132, 305, 205
+130, 298, 201
+126, 291, 196
+123, 283, 191
+120, 275, 185
+116, 267, 180
+112, 258, 174
+108, 249, 168
 104, 239, 161
-99, 229, 154
-95, 219, 147
-90, 209, 140
-86, 198, 133
+100, 230, 155
+95, 220, 148
+91, 209, 141
+86, 198, 134
 81, 187, 126
 76, 175, 118
 71, 163, 110
-65, 150, 101
-59, 137, 92
-54, 124, 83
-47, 109, 74
-41, 94, 63
-34, 78, 52
-26, 61, 41
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
+66, 151, 102
+60, 138, 93
+54, 124, 84
+48, 110, 74
+41, 95, 64
+34, 79, 53
+27, 61, 41
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1165,40 +1165,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-134, 308, 207
-139, 320, 215
-139, 321, 216
-138, 319, 215
+134, 308, 208
+139, 320, 216
+140, 321, 217
+139, 319, 215
 137, 315, 212
-134, 309, 208
-132, 303, 204
+135, 310, 209
+132, 304, 205
 129, 297, 200
 126, 290, 195
-122, 282, 190
-119, 274, 184
-115, 265, 179
-111, 257, 173
-107, 247, 167
-103, 238, 160
-99, 228, 154
-95, 218, 147
-90, 208, 140
-85, 197, 133
-81, 186, 125
-76, 174, 117
-70, 162, 109
-65, 150, 101
-59, 137, 92
-53, 124, 83
-47, 110, 74
+123, 282, 190
+119, 274, 185
+116, 266, 179
+112, 257, 173
+108, 248, 167
+104, 238, 161
+99, 229, 154
+95, 219, 147
+91, 208, 140
+86, 197, 133
+81, 186, 126
+76, 175, 118
+71, 163, 110
+65, 151, 101
+60, 138, 93
+54, 124, 84
+48, 110, 74
 41, 95, 64
 34, 79, 53
-27, 62, 41
-19, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 62, 42
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1229,40 +1229,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-137, 315, 212
-139, 321, 216
-139, 321, 216
-138, 318, 214
-136, 314, 211
-134, 308, 208
-131, 302, 203
-128, 295, 199
-125, 288, 194
-122, 280, 189
-118, 272, 183
-114, 264, 178
-111, 255, 172
+137, 316, 213
+140, 322, 217
+140, 321, 217
+139, 319, 215
+137, 314, 212
+134, 309, 208
+132, 303, 204
+129, 296, 199
+125, 289, 195
+122, 281, 189
+119, 273, 184
+115, 264, 178
+111, 256, 172
 107, 246, 166
-103, 237, 159
-98, 227, 153
+103, 237, 160
+99, 227, 153
 94, 217, 146
-89, 206, 139
+90, 207, 139
 85, 196, 132
-80, 185, 124
-75, 173, 117
-70, 161, 109
-65, 149, 100
-59, 136, 92
-53, 123, 83
-47, 109, 73
-41, 94, 63
+81, 185, 125
+76, 174, 117
+70, 162, 109
+65, 150, 101
+59, 137, 92
+54, 124, 83
+48, 110, 74
+41, 95, 64
 34, 79, 53
-27, 62, 42
-19, 44, 30
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 63, 42
+19, 45, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1293,40 +1293,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-138, 317, 214
+138, 318, 214
+140, 322, 217
 139, 321, 216
-139, 320, 216
-137, 317, 213
-135, 312, 210
-133, 307, 207
-130, 300, 202
-127, 294, 198
-124, 286, 193
+138, 317, 214
+136, 313, 211
+134, 307, 207
+131, 301, 203
+128, 294, 198
+125, 287, 193
 121, 279, 188
-117, 270, 182
-114, 262, 176
-110, 253, 170
-106, 244, 164
-102, 235, 158
-98, 225, 151
-93, 215, 145
+118, 271, 183
+114, 262, 177
+110, 254, 171
+106, 245, 165
+102, 235, 159
+98, 226, 152
+94, 216, 145
 89, 205, 138
-84, 194, 131
-79, 183, 123
-74, 172, 116
-69, 160, 108
-64, 148, 99
-58, 135, 91
+85, 195, 131
+80, 184, 124
+75, 172, 116
+70, 160, 108
+64, 148, 100
+59, 136, 91
 53, 122, 82
 47, 108, 73
-40, 93, 63
-34, 78, 52
-27, 62, 41
-19, 44, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 63
+34, 79, 53
+27, 62, 42
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1356,42 +1356,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-130, 299, 201
+130, 299, 202
 138, 318, 214
-139, 320, 215
-138, 318, 214
-137, 315, 212
-135, 310, 209
-132, 305, 205
-129, 298, 201
-126, 291, 196
-123, 284, 191
-120, 276, 186
-116, 268, 181
+139, 320, 216
+139, 319, 215
+137, 315, 213
+135, 311, 209
+133, 305, 206
+130, 299, 201
+127, 292, 197
+124, 284, 192
+120, 277, 186
+117, 269, 181
 113, 260, 175
 109, 251, 169
 105, 242, 163
-101, 232, 157
+101, 233, 157
 97, 223, 150
-92, 213, 143
-88, 203, 136
-83, 192, 129
-78, 181, 122
-73, 170, 114
-68, 158, 106
-63, 146, 98
-58, 133, 90
-52, 120, 81
-46, 106, 72
+93, 213, 144
+88, 203, 137
+84, 192, 130
+79, 182, 122
+74, 170, 115
+69, 158, 107
+64, 146, 99
+58, 134, 90
+52, 121, 81
+46, 107, 72
 40, 92, 62
-33, 77, 52
-26, 60, 41
-18, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+34, 77, 52
+27, 61, 41
+19, 44, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1420,42 +1420,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-132, 304, 205
-137, 316, 213
-138, 318, 214
-137, 316, 213
-136, 312, 210
-133, 308, 207
-131, 302, 203
-128, 296, 199
-125, 289, 194
-122, 281, 189
-119, 274, 184
-115, 265, 179
-111, 257, 173
-108, 248, 167
-104, 239, 161
+132, 305, 205
+138, 317, 213
+138, 318, 215
+138, 317, 213
+136, 313, 211
+134, 308, 208
+131, 302, 204
+129, 296, 199
+126, 289, 195
+123, 282, 190
+119, 274, 185
+116, 266, 179
+112, 258, 174
+108, 249, 168
+104, 240, 162
 100, 230, 155
-95, 220, 148
-91, 210, 142
-87, 200, 135
-82, 189, 128
-77, 179, 120
-72, 167, 113
-67, 156, 105
-62, 144, 97
-57, 131, 88
-51, 118, 79
-45, 104, 70
-39, 90, 60
-32, 75, 50
-25, 59, 39
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+96, 221, 149
+92, 211, 142
+87, 201, 135
+83, 190, 128
+78, 179, 121
+73, 168, 113
+68, 156, 105
+63, 144, 97
+57, 132, 89
+52, 118, 80
+46, 105, 71
+39, 90, 61
+33, 75, 51
+26, 59, 40
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1474,7 +1474,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-69, 69, 69
+70, 70, 70
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1485,41 +1485,41 @@
 255, 255, 255
 255, 255, 255
 132, 304, 205
+137, 314, 212
+137, 316, 213
 136, 314, 211
-137, 315, 212
-136, 313, 211
-134, 309, 208
-132, 304, 205
-130, 299, 201
-127, 292, 197
-124, 286, 192
-121, 278, 187
-117, 270, 182
-114, 262, 177
-110, 254, 171
-106, 245, 165
-102, 236, 159
-98, 227, 153
-94, 217, 146
-90, 207, 140
-85, 197, 133
+135, 310, 209
+133, 305, 206
+130, 299, 202
+127, 293, 197
+124, 286, 193
+121, 279, 188
+118, 271, 183
+114, 263, 177
+111, 254, 171
+107, 246, 166
+103, 237, 160
+99, 227, 153
+95, 218, 147
+90, 208, 140
+86, 198, 133
 81, 187, 126
-76, 176, 118
-71, 165, 111
-66, 153, 103
+77, 176, 119
+72, 165, 111
+67, 153, 103
 61, 141, 95
-56, 128, 86
-50, 115, 78
-44, 102, 68
-38, 87, 59
-31, 72, 49
-24, 56, 38
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+56, 129, 87
+50, 116, 78
+44, 102, 69
+38, 88, 59
+32, 73, 49
+25, 57, 38
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1549,41 +1549,41 @@
 255, 255, 255
 255, 255, 255
 131, 301, 203
+135, 311, 209
+136, 312, 210
 135, 310, 209
-135, 311, 210
-134, 309, 208
 133, 306, 206
 131, 301, 203
-128, 295, 199
-125, 289, 195
-122, 282, 190
-119, 275, 185
-116, 267, 180
-112, 259, 174
+129, 296, 199
+126, 289, 195
+123, 283, 190
+120, 275, 185
+116, 268, 180
+113, 259, 175
 109, 251, 169
 105, 242, 163
 101, 233, 157
 97, 224, 151
-93, 214, 144
-88, 204, 137
-84, 194, 131
-79, 183, 123
-75, 173, 116
-70, 161, 109
+93, 215, 145
+89, 205, 138
+85, 194, 131
+80, 184, 124
+75, 173, 117
+70, 162, 109
 65, 150, 101
 60, 138, 93
-54, 125, 84
-49, 112, 75
-43, 99, 66
-36, 84, 57
-30, 69, 47
-23, 53, 36
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+55, 126, 85
+49, 113, 76
+43, 99, 67
+37, 85, 57
+30, 70, 47
+23, 54, 36
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1612,42 +1612,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-129, 296, 199
+129, 297, 200
 133, 306, 206
-133, 307, 207
-132, 305, 206
-131, 302, 203
+134, 308, 207
+133, 306, 206
+131, 302, 204
 129, 297, 200
-126, 291, 196
+127, 292, 197
 124, 285, 192
-121, 278, 187
-117, 271, 182
-114, 263, 177
-111, 255, 172
-107, 247, 166
-103, 238, 160
-99, 229, 154
-95, 220, 148
-91, 210, 142
-87, 200, 135
-82, 190, 128
-78, 180, 121
-73, 169, 114
-68, 158, 106
-63, 146, 98
-58, 134, 90
+121, 279, 188
+118, 271, 183
+115, 264, 178
+111, 256, 172
+108, 247, 167
+104, 239, 161
+100, 230, 155
+96, 220, 149
+92, 211, 142
+87, 201, 135
+83, 191, 129
+78, 180, 122
+74, 169, 114
+69, 158, 107
+64, 147, 99
+59, 135, 91
 53, 122, 82
-47, 109, 73
-41, 95, 64
-35, 81, 54
-28, 66, 44
-21, 50, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+47, 109, 74
+42, 96, 64
+35, 81, 55
+29, 66, 45
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1676,42 +1676,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-126, 289, 195
-130, 300, 202
-131, 302, 203
-130, 300, 202
+126, 290, 195
+131, 301, 203
+132, 303, 204
+131, 301, 203
 129, 297, 200
-127, 292, 197
-124, 287, 193
+127, 293, 197
+125, 287, 194
 122, 281, 189
-119, 274, 184
-116, 267, 179
-112, 259, 174
+119, 274, 185
+116, 267, 180
+113, 259, 175
 109, 251, 169
-105, 243, 163
-101, 234, 158
-98, 225, 152
-94, 216, 145
-89, 206, 139
-85, 196, 132
-81, 186, 125
-76, 176, 118
-71, 165, 111
-67, 154, 103
-62, 142, 96
-56, 130, 88
-51, 118, 79
-45, 105, 70
-39, 91, 61
-33, 77, 52
-27, 62, 41
+106, 243, 164
+102, 235, 158
+98, 226, 152
+94, 216, 146
+90, 207, 139
+86, 197, 133
+81, 187, 126
+77, 176, 119
+72, 166, 112
+67, 154, 104
+62, 143, 96
+57, 131, 88
+51, 118, 80
+46, 105, 71
+40, 92, 62
+34, 77, 52
+27, 62, 42
 20, 46, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1740,42 +1740,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-121, 280, 188
+122, 280, 189
 128, 294, 198
-129, 296, 199
+129, 297, 200
 128, 295, 199
-127, 292, 196
-125, 287, 193
-122, 282, 190
+127, 292, 197
+125, 288, 194
+123, 282, 190
 120, 276, 186
-117, 269, 181
-114, 262, 176
-110, 254, 171
-107, 246, 166
-103, 238, 160
+117, 270, 182
+114, 262, 177
+111, 255, 172
+107, 247, 166
+104, 239, 161
 100, 230, 155
 96, 221, 149
-92, 211, 142
+92, 212, 143
 88, 202, 136
-83, 192, 129
-79, 182, 122
-74, 171, 115
-70, 161, 108
-65, 149, 101
+84, 193, 130
+79, 182, 123
+75, 172, 116
+70, 161, 109
+65, 150, 101
 60, 138, 93
-54, 126, 85
-49, 113, 76
-43, 100, 67
-37, 87, 58
-31, 72, 49
-25, 57, 38
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+55, 126, 85
+49, 114, 77
+44, 101, 68
+38, 87, 59
+32, 73, 49
+25, 58, 39
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1805,40 +1805,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-124, 286, 193
+125, 287, 193
 126, 290, 195
-125, 289, 194
-124, 286, 192
-122, 282, 190
-120, 276, 186
-117, 270, 182
-114, 264, 178
-111, 257, 173
-108, 249, 168
-105, 241, 163
-101, 233, 157
-97, 225, 151
-94, 216, 145
-90, 207, 139
-85, 197, 133
-81, 187, 126
-77, 177, 119
-72, 167, 112
-67, 156, 105
-63, 145, 97
-58, 133, 90
-52, 121, 81
-47, 108, 73
-41, 95, 64
-35, 82, 55
-29, 67, 45
-22, 52, 35
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+126, 289, 195
+124, 286, 193
+123, 282, 190
+120, 277, 187
+118, 271, 183
+115, 264, 178
+112, 257, 173
+109, 250, 168
+105, 242, 163
+102, 234, 158
+98, 225, 152
+94, 216, 146
+90, 207, 140
+86, 198, 133
+82, 188, 127
+77, 178, 120
+73, 167, 113
+68, 156, 105
+63, 145, 98
+58, 134, 90
+53, 122, 82
+47, 109, 73
+42, 96, 65
+36, 82, 55
+29, 68, 46
+23, 53, 35
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1869,40 +1869,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
+120, 277, 187
+123, 282, 190
+123, 282, 190
+122, 280, 189
 120, 276, 186
-122, 282, 190
-122, 282, 190
-121, 279, 188
-119, 275, 185
-117, 270, 182
-115, 265, 178
-112, 258, 174
-109, 251, 169
-106, 244, 164
-102, 236, 159
-99, 228, 153
-95, 219, 148
-91, 211, 142
-87, 201, 136
-83, 192, 129
-79, 182, 123
-75, 172, 116
+118, 271, 183
+115, 265, 179
+112, 259, 174
+109, 252, 170
+106, 244, 165
+103, 237, 159
+99, 228, 154
+96, 220, 148
+92, 211, 142
+88, 202, 136
+84, 192, 130
+79, 183, 123
+75, 173, 116
 70, 162, 109
-65, 151, 101
-60, 139, 94
-55, 128, 86
-50, 116, 78
-45, 103, 69
-39, 90, 60
-33, 76, 51
-26, 62, 41
-20, 46, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+66, 151, 102
+61, 140, 94
+56, 128, 86
+51, 116, 78
+45, 104, 70
+39, 90, 61
+33, 77, 52
+27, 62, 42
+20, 47, 31
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1933,40 +1933,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-114, 264, 178
+115, 264, 178
+119, 273, 184
+119, 274, 185
 118, 273, 184
-119, 274, 184
-118, 272, 183
-116, 268, 181
-114, 264, 178
-112, 258, 174
-109, 252, 170
-106, 245, 165
-103, 238, 160
-100, 230, 155
-96, 222, 150
+117, 269, 181
+115, 264, 178
+112, 259, 174
+110, 252, 170
+107, 246, 166
+104, 238, 161
+100, 231, 155
+97, 223, 150
 93, 214, 144
 89, 205, 138
 85, 196, 132
-81, 186, 125
-76, 177, 119
-72, 166, 112
-67, 156, 105
-63, 145, 98
+81, 187, 126
+77, 177, 119
+73, 167, 112
+68, 156, 105
+63, 146, 98
 58, 134, 90
-53, 122, 82
+53, 123, 83
 48, 110, 74
-42, 97, 65
-36, 84, 56
-30, 70, 47
-24, 55, 37
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+42, 98, 66
+37, 84, 57
+31, 71, 48
+24, 56, 38
 17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1997,40 +1997,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-105, 243, 163
-113, 262, 176
-115, 265, 178
-114, 264, 178
-113, 261, 176
-111, 256, 173
-109, 251, 169
-106, 245, 165
-103, 239, 161
-100, 231, 156
-97, 224, 151
-94, 216, 145
-90, 207, 140
-86, 199, 134
-82, 190, 128
-78, 180, 121
-74, 170, 115
-69, 160, 108
+106, 243, 164
+114, 262, 177
+115, 265, 179
+115, 264, 178
+114, 261, 176
+112, 257, 173
+109, 252, 170
+107, 246, 166
+104, 239, 161
+101, 232, 156
+98, 224, 151
+94, 216, 146
+90, 208, 140
+87, 199, 134
+83, 190, 128
+79, 181, 122
+74, 171, 115
+70, 161, 108
 65, 150, 101
-60, 139, 93
-55, 128, 86
-50, 116, 78
+61, 139, 94
+56, 128, 86
+51, 116, 78
 45, 104, 70
-39, 91, 61
-33, 77, 52
-27, 63, 42
-21, 48, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+40, 91, 62
+34, 78, 52
+28, 64, 43
+21, 49, 33
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2062,38 +2062,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-107, 247, 167
-110, 254, 171
-110, 254, 171
-109, 252, 170
 108, 248, 167
+111, 254, 171
+111, 255, 172
+110, 253, 170
+108, 249, 168
 106, 244, 164
-103, 238, 160
-100, 231, 156
-97, 224, 151
-94, 217, 146
-91, 209, 141
-87, 201, 135
-83, 192, 129
-79, 183, 123
-75, 174, 117
-71, 164, 110
-67, 154, 103
-62, 143, 96
-57, 132, 89
-52, 121, 81
-47, 109, 73
+104, 238, 161
+101, 232, 156
+98, 225, 152
+95, 217, 147
+91, 210, 141
+87, 201, 136
+84, 193, 130
+80, 183, 124
+76, 174, 117
+71, 164, 111
+67, 154, 104
+62, 144, 97
+58, 133, 89
+53, 121, 82
+48, 109, 74
 42, 97, 65
-36, 84, 56
-30, 70, 47
-24, 56, 37
-17, 41, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+37, 84, 57
+31, 71, 48
+24, 56, 38
+18, 41, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2126,38 +2126,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-97, 224, 151
-104, 241, 162
+98, 224, 151
+105, 241, 163
+106, 244, 164
 106, 243, 164
-105, 242, 163
-104, 239, 161
-102, 235, 158
+104, 240, 162
+102, 235, 159
 100, 230, 155
-97, 223, 150
+97, 224, 151
 94, 217, 146
-91, 209, 141
-87, 202, 136
-84, 193, 130
-80, 185, 124
-76, 176, 118
-72, 166, 112
-68, 157, 105
-63, 146, 99
-59, 136, 91
-54, 125, 84
-49, 113, 76
-44, 101, 68
-38, 89, 60
+91, 210, 141
+88, 202, 136
+84, 194, 131
+81, 185, 125
+77, 176, 119
+73, 167, 112
+68, 157, 106
+64, 147, 99
+59, 136, 92
+55, 125, 84
+50, 114, 77
+44, 102, 69
+39, 89, 60
 33, 76, 51
-27, 62, 42
-20, 47, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 63, 42
+21, 48, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2191,36 +2191,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-96, 222, 150
-100, 230, 155
+97, 223, 150
 100, 231, 156
-99, 229, 154
-98, 225, 152
-96, 220, 148
-93, 215, 144
-90, 208, 140
-87, 201, 135
-84, 193, 130
-80, 185, 125
-77, 177, 119
+101, 232, 156
+100, 230, 155
+98, 226, 152
+96, 221, 149
+94, 215, 145
+91, 209, 141
+88, 202, 136
+84, 194, 131
+81, 186, 125
+77, 177, 120
 73, 168, 113
 69, 159, 107
-64, 149, 100
-60, 139, 93
-55, 128, 86
+65, 149, 101
+60, 139, 94
+56, 128, 87
 51, 117, 79
-46, 105, 71
-40, 93, 63
-35, 81, 54
-29, 67, 45
-23, 53, 36
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+46, 106, 71
+41, 94, 63
+35, 81, 55
+29, 68, 46
+23, 54, 36
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2256,34 +2256,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-92, 213, 143
-94, 217, 146
-94, 217, 146
-93, 214, 144
-91, 210, 141
+93, 213, 144
+95, 218, 147
+95, 218, 147
+93, 215, 145
+92, 211, 142
 89, 205, 138
-86, 199, 134
-83, 192, 129
-80, 184, 124
-76, 177, 119
-73, 168, 113
-69, 159, 107
+87, 199, 134
+84, 192, 130
+80, 185, 125
+77, 177, 119
+73, 169, 114
+69, 160, 108
 65, 150, 101
-61, 140, 94
-56, 130, 87
-52, 119, 80
-47, 108, 73
-42, 96, 65
-36, 84, 57
-31, 71, 48
+61, 141, 95
+57, 130, 88
+52, 120, 81
+47, 109, 73
+42, 97, 65
+37, 85, 57
+31, 72, 48
 25, 58, 39
-18, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+19, 44, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2320,34 +2320,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-75, 173, 117
-86, 199, 134
-88, 203, 136
-87, 202, 136
-86, 198, 134
-84, 194, 130
-82, 188, 127
-79, 182, 122
 76, 174, 117
-72, 167, 112
-69, 158, 107
+87, 200, 135
+88, 203, 137
+88, 202, 136
+86, 199, 134
+84, 194, 131
+82, 189, 127
+79, 182, 123
+76, 175, 118
+73, 167, 113
+69, 159, 107
 65, 150, 101
-61, 140, 94
+61, 141, 95
 57, 131, 88
-52, 120, 81
-47, 110, 74
-42, 98, 66
-37, 86, 58
+53, 121, 81
+48, 110, 74
+43, 99, 67
+38, 87, 59
 32, 74, 50
-26, 61, 41
-20, 47, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 61, 41
+21, 47, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2385,32 +2385,32 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-71, 165, 111
-79, 183, 123
-80, 186, 125
+72, 165, 111
 80, 184, 124
-78, 181, 122
-76, 176, 118
-74, 170, 114
-71, 163, 110
-67, 156, 105
-64, 148, 99
-60, 139, 93
-56, 130, 87
+81, 186, 126
+80, 185, 125
+79, 181, 122
+77, 176, 119
+74, 170, 115
+71, 164, 110
+68, 156, 105
+64, 148, 100
+61, 139, 94
+57, 130, 88
 52, 120, 81
-47, 110, 74
-43, 99, 66
-38, 87, 59
-32, 75, 51
-27, 62, 42
+48, 110, 74
+43, 99, 67
+38, 88, 59
+33, 76, 51
+27, 63, 42
 21, 49, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2451,28 +2451,28 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-71, 164, 110
-72, 167, 112
-72, 165, 111
+71, 164, 111
+73, 167, 113
+72, 166, 112
 70, 162, 109
-68, 156, 105
-65, 150, 101
-62, 143, 96
-59, 135, 91
-55, 127, 85
-51, 118, 79
+68, 157, 106
+66, 151, 102
+62, 144, 97
+59, 136, 91
+55, 127, 86
+51, 118, 80
 47, 108, 73
-42, 98, 66
-37, 87, 58
-32, 75, 50
-27, 62, 42
-21, 49, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+43, 98, 66
+38, 87, 59
+33, 75, 51
+27, 63, 42
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2516,26 +2516,26 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-60, 140, 94
-62, 144, 97
-62, 143, 96
-60, 140, 94
-58, 135, 91
-55, 128, 86
-52, 121, 81
+61, 140, 94
+63, 145, 98
+63, 144, 97
+61, 140, 95
+59, 135, 91
+56, 129, 87
+53, 121, 82
 49, 113, 76
 45, 104, 70
-41, 94, 63
-36, 83, 56
-31, 72, 49
-26, 60, 40
-20, 47, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 64
+37, 84, 57
+32, 73, 49
+26, 61, 41
+21, 48, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2582,22 +2582,22 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-50, 116, 78
 51, 117, 79
-49, 114, 77
-47, 109, 74
-44, 103, 69
-41, 95, 64
-37, 87, 58
-33, 77, 52
-29, 66, 45
+51, 118, 79
+50, 115, 77
+48, 110, 74
+45, 103, 70
+42, 96, 65
+38, 87, 59
+34, 77, 52
+29, 67, 45
 24, 55, 37
-18, 42, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+19, 43, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2648,18 +2648,18 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-35, 82, 55
-35, 82, 55
-34, 78, 53
-31, 72, 48
+36, 82, 55
+36, 83, 56
+34, 79, 53
+31, 72, 49
 28, 64, 43
-23, 55, 37
-19, 44, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+24, 55, 37
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2716,10 +2716,10 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
diff --git a/examples/ml/raytracer.effekt b/examples/ml/raytracer.effekt
index 8a5febc4..cd3e7f10 100644
--- a/examples/ml/raytracer.effekt
+++ b/examples/ml/raytracer.effekt
@@ -74,7 +74,7 @@ val WHITE = Vector(255.0, 255.0, 255.0)
 def toVector(c: Color): ColorV = Vector(c.red.toDouble, c.green.toDouble, c.blue.toDouble)
 
 // TODO here we should check for the range of 0-255
-def toColor(c: ColorV): Color = Color(c.x.round, c.y.round, c.z.round)
+def toColor(c: ColorV): Color = Color(c.x.toInt, c.y.toInt, c.z.toInt)
 
 type Light = Point
 
diff --git a/examples/neg/ambiguous_overload.check b/examples/neg/ambiguous_overload.check
index 6596b957..1763bfb8 100644
--- a/examples/neg/ambiguous_overload.check
+++ b/examples/neg/ambiguous_overload.check
@@ -18,4 +18,4 @@ Possible overload: effekt::infixAdd of type (Double, Double) => Double
     Expected Double but got Int.
 
 def main() = foo(1) + 2 // nested overload!
-             ^^^^^^^
+             ^^^^^^
diff --git a/examples/neg/def_instead_of_val.check b/examples/neg/def_instead_of_val.check
index 0569b9a2..1b2b27c2 100644
--- a/examples/neg/def_instead_of_val.check
+++ b/examples/neg/def_instead_of_val.check
@@ -1,4 +1,4 @@
 [error] examples/neg/def_instead_of_val.effekt:3:13: Expected the right-hand side of a `def` binding to be a block, but got a value of type Int.
 Maybe try `val` if you're defining a value.
 def three = addOne(2)
-            ^
+            ^^^^^^^^^
diff --git a/examples/neg/effect_not_part.check b/examples/neg/effect_not_part.check
index 5d54cb60..1242722e 100644
--- a/examples/neg/effect_not_part.check
+++ b/examples/neg/effect_not_part.check
@@ -1,3 +1,3 @@
 [error] examples/neg/effect_not_part.effekt:17:9: Operation raise is not part of interface State.
         def raise() = ()
-        ^
+        ^^^^^^^^^^^^^^^^
diff --git a/examples/neg/effekt_implemented_extern.check b/examples/neg/effekt_implemented_extern.check
index fb897712..cd619fac 100644
--- a/examples/neg/effekt_implemented_extern.check
+++ b/examples/neg/effekt_implemented_extern.check
@@ -1,3 +1,3 @@
 [error] examples/neg/effekt_implemented_extern.effekt:4:8: Used captures {io} are not in the allowed set {}
   js { test() }
-       ^^^^^^^
+       ^^^^^^
diff --git a/examples/neg/handler_operation_annotation.check b/examples/neg/handler_operation_annotation.check
index c4fed38e..328e5c9d 100644
--- a/examples/neg/handler_operation_annotation.check
+++ b/examples/neg/handler_operation_annotation.check
@@ -1,3 +1,3 @@
 [error] examples/neg/handler_operation_annotation.effekt:7:5: Unexpected type annotation on operation read.
     def read(): String = "Hello, world!"
-    ^
+    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/neg/inference.check b/examples/neg/inference.check
index 2a76275e..f9038762 100644
--- a/examples/neg/inference.check
+++ b/examples/neg/inference.check
@@ -12,11 +12,8 @@ def test2(l1: List[Int]): Int = l1 match {
 [error] examples/neg/inference.effekt:20:17: Expected Int but got String.
   case Nil() => ""
                 ^^
-[error] examples/neg/inference.effekt:20:17: Expected Int but got String.
-  case Nil() => ""
-                ^
 [error] examples/neg/inference.effekt:51:25: Expected List[String] but got List[Int].
 
 Type mismatch between String and Int.
   case Cons(a, rest) => Nil[Int]() // failing to check the call here aborts type checking, so this example goes last
-                        ^
+                        ^^^^^^^^^^
diff --git a/examples/neg/leakingtypevars.check b/examples/neg/leakingtypevars.check
index 7e66349b..fdebc3c2 100644
--- a/examples/neg/leakingtypevars.check
+++ b/examples/neg/leakingtypevars.check
@@ -1,3 +1,3 @@
 [error] examples/neg/leakingtypevars.effekt:12:5: Expected Int but got R.
     p()
-    ^
+    ^^^
diff --git a/examples/neg/match_unreachable.check b/examples/neg/match_unreachable.check
index 70a910a3..8b9f9243 100644
--- a/examples/neg/match_unreachable.check
+++ b/examples/neg/match_unreachable.check
@@ -1,3 +1,3 @@
 [warning] examples/neg/match_unreachable.effekt:5:3: Unreachable case.
   case Cons(_, Cons(_, _)) => 1
-  ^
+  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/neg/matchdef.check b/examples/neg/matchdef.check
index f87e0f77..caf64a19 100644
--- a/examples/neg/matchdef.check
+++ b/examples/neg/matchdef.check
@@ -1,3 +1,3 @@
-[error] examples/neg/matchdef.effekt:13:9: Non-exhaustive pattern matching, missing case Other()
+[error] examples/neg/matchdef.effekt:13:5: Non-exhaustive pattern matching, missing case Other()
     val Person(name, age) = r;
-        ^^^^^^^^^^^^^^^^^
+    ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/neg/multiline_extern_definition.check b/examples/neg/multiline_extern_definition.check
index 9bf368af..3c8d1523 100644
--- a/examples/neg/multiline_extern_definition.check
+++ b/examples/neg/multiline_extern_definition.check
@@ -1,3 +1,3 @@
-[error] examples/neg/multiline_extern_definition.effekt:1:38: Expected an extern definition, which can either be a single-line string (e.g., "x + y") or a multi-line string (e.g., """..."""), potentially qualified
+[error] examples/neg/multiline_extern_definition.effekt:1:38: Unterminated string.
 extern def jsPrintThree(): Unit = js """
-                                     ^
+                                     ^^^
diff --git a/examples/neg/multiline_string.check b/examples/neg/multiline_string.check
index fcf47fec..79859ed7 100644
--- a/examples/neg/multiline_string.check
+++ b/examples/neg/multiline_string.check
@@ -1,3 +1,3 @@
-[error] examples/neg/multiline_string.effekt:1:22: ')' expected but '"' found
+[error] examples/neg/multiline_string.effekt:1:22: Linebreaks are not allowed in single-line strings.
 def main() = println("a
-                     ^
+                     ^^
diff --git a/examples/neg/nomain.check b/examples/neg/nomain.check
index ab1a2880..653ef287 100644
--- a/examples/neg/nomain.check
+++ b/examples/neg/nomain.check
@@ -1,3 +1,3 @@
 [error] examples/neg/nomain.effekt:1:1: No main function defined
 module nomain
-^
+^^^^^^^^^^^^^
diff --git a/examples/neg/object/obj_unbound_block.check b/examples/neg/object/obj_unbound_block.check
index 93681845..6c5a26c1 100644
--- a/examples/neg/object/obj_unbound_block.check
+++ b/examples/neg/object/obj_unbound_block.check
@@ -1,3 +1,3 @@
 [error] examples/neg/object/obj_unbound_block.effekt:3:21: Number of block parameters (0) does not match declaration of 'Eff', which expects 2.
 def impl: Eff = new Eff { (a: Int) =>
-                    ^^^
+                    ^
diff --git a/examples/neg/object_operation_annotation.check b/examples/neg/object_operation_annotation.check
index a6f2c6bf..ac7b7a17 100644
--- a/examples/neg/object_operation_annotation.check
+++ b/examples/neg/object_operation_annotation.check
@@ -1,3 +1,3 @@
 [error] examples/neg/object_operation_annotation.effekt:5:5: Unexpected type annotation on operation bar.
     def bar(): Int = 42
-    ^
+    ^^^^^^^^^^^^^^^^^^^
diff --git a/examples/neg/parsing/numbers.check b/examples/neg/parsing/numbers.check
index b79c3873..c59e94bc 100644
--- a/examples/neg/parsing/numbers.check
+++ b/examples/neg/parsing/numbers.check
@@ -1,3 +1,3 @@
-[error] examples/neg/parsing/numbers.effekt:2:30: Not a 64bit integer literal.
+[error] examples/neg/parsing/numbers.effekt:2:11: Not a 64bit integer literal.
   println(9223372036854775808)
-                             ^
+          ^^^^^^^^^^^^^^^^^^^
diff --git a/examples/neg/polymorphic_adts.check b/examples/neg/polymorphic_adts.check
index 21767445..9c061093 100644
--- a/examples/neg/polymorphic_adts.check
+++ b/examples/neg/polymorphic_adts.check
@@ -12,4 +12,4 @@ Possible overload: effekt::infixAdd of type (Double, Double) => Double
   Expected Double but got Int.
 
         case Pair2(n, t) => if (t) { n + 1 } else { 0 }
-                                     ^^^^^^
+                                     ^^^^^
diff --git a/examples/neg/selecting_from_datatype.check b/examples/neg/selecting_from_datatype.check
index 2722ea0a..54b1bf73 100644
--- a/examples/neg/selecting_from_datatype.check
+++ b/examples/neg/selecting_from_datatype.check
@@ -1,3 +1,3 @@
 [error] examples/neg/selecting_from_datatype.effekt:8:22: Cannot find type for myHead -- forward uses and recursive functions need annotated return types.
 def foo(l: MyList) = myHead(l)
-                     ^
+                     ^^^^^^^^^
diff --git a/examples/neg/toplevel_effects.check b/examples/neg/toplevel_effects.check
index e2f1f1e9..3b041a40 100644
--- a/examples/neg/toplevel_effects.check
+++ b/examples/neg/toplevel_effects.check
@@ -1,3 +1,3 @@
 [error] examples/neg/toplevel_effects.effekt:8:28: Effect Console is not allowed in this context.
 def pureFun(): Unit / {} = sayHello()
-                           ^
+                           ^^^^^^^^^^
diff --git a/examples/neg/typealias.check b/examples/neg/typealias.check
index c7f52527..3b1b637c 100644
--- a/examples/neg/typealias.check
+++ b/examples/neg/typealias.check
@@ -10,4 +10,4 @@ Possible overload: effekt::infixAdd of type (Double, Double) => Double
   Expected Double but got Int.
 
 def add(x: Foo, y: Foo): Bar = x + y
-                               ^
+                               ^^^^^
diff --git a/examples/neg/typer/no-resume-in-object.check b/examples/neg/typer/no-resume-in-object.check
index 0aa59334..a113ecb6 100644
--- a/examples/neg/typer/no-resume-in-object.check
+++ b/examples/neg/typer/no-resume-in-object.check
@@ -1,3 +1,3 @@
 [error] examples/neg/typer/no-resume-in-object.effekt:5:22: Cannot find `resume`. Maybe you are trying to resume inside of an object literal and not as part of `try { ... } with ...`?
     def print(msg) = resume(())
-                     ^
+                     ^^^^^^^^^^
diff --git a/examples/neg/variables.check b/examples/neg/variables.check
index c90ddf48..764f1bfc 100644
--- a/examples/neg/variables.check
+++ b/examples/neg/variables.check
@@ -1,3 +1,3 @@
 [error] examples/neg/variables.effekt:5:3: Can only assign to mutable variables, but x is a constant.
   x = 9
-  ^
+  ^^^^^
diff --git a/examples/pos/bidirectional/selfrecursion.check b/examples/pos/bidirectional/selfrecursion.check
index f0fee3a4..ec8356fb 100644
--- a/examples/pos/bidirectional/selfrecursion.check
+++ b/examples/pos/bidirectional/selfrecursion.check
@@ -1,3 +1,3 @@
 [error] examples/pos/bidirectional/selfrecursion.effekt:3:1: Bidirectional effects that mention the same effect recursively are not (yet) supported.
 effect Loop(): Unit / Loop
-^
+^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/pos/escaped_linebreaks.check b/examples/pos/escaped_linebreaks.check
deleted file mode 100644
index 2a308ce1..00000000
--- a/examples/pos/escaped_linebreaks.check
+++ /dev/null
@@ -1,1 +0,0 @@
-a    "b"	cd
diff --git a/examples/pos/escaped_linebreaks.effekt b/examples/pos/escaped_linebreaks.effekt
deleted file mode 100644
index 464f76a0..00000000
--- a/examples/pos/escaped_linebreaks.effekt
+++ /dev/null
@@ -1,4 +0,0 @@
-def main() = println("a\
-    \"b\"\
-	c\
-d")
diff --git a/examples/pos/patternmatching/matching-defaults.check b/examples/pos/patternmatching/matching-defaults.check
index 25b9ad7c..5744cbcc 100644
--- a/examples/pos/patternmatching/matching-defaults.check
+++ b/examples/pos/patternmatching/matching-defaults.check
@@ -1,4 +1,4 @@
 default
 [warning] examples/pos/patternmatching/matching-defaults.effekt:5:3: Unreachable case.
   case None() => "world"
-  ^
+  ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/pos/raytracer.check b/examples/pos/raytracer.check
index e2431864..1c6bb9b2 100644
--- a/examples/pos/raytracer.check
+++ b/examples/pos/raytracer.check
@@ -474,13 +474,13 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-81, 187, 126
-80, 185, 125
-77, 178, 120
-73, 168, 113
-68, 157, 105
-62, 143, 96
-55, 126, 85
+82, 188, 127
+81, 186, 125
+78, 179, 120
+73, 169, 114
+68, 157, 106
+62, 143, 97
+55, 127, 85
 45, 104, 70
 255, 255, 255
 255, 255, 255
@@ -535,20 +535,20 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-98, 227, 153
-99, 228, 153
-97, 223, 150
-94, 216, 146
-90, 208, 140
-86, 199, 134
-82, 188, 127
-77, 177, 119
-71, 165, 111
-65, 151, 102
-59, 136, 91
-51, 119, 80
-43, 99, 67
-32, 73, 49
+99, 227, 153
+99, 228, 154
+97, 224, 151
+94, 217, 146
+91, 209, 141
+87, 199, 134
+82, 189, 127
+77, 178, 120
+72, 165, 111
+66, 151, 102
+59, 136, 92
+52, 120, 81
+43, 100, 67
+32, 74, 50
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -597,24 +597,24 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
+109, 251, 169
+110, 253, 170
 108, 250, 168
-109, 252, 170
-108, 249, 168
 106, 244, 164
-103, 237, 159
-99, 228, 154
-95, 220, 148
-91, 210, 141
-86, 199, 134
-81, 188, 127
-76, 176, 119
-71, 163, 110
+103, 237, 160
+100, 229, 154
+96, 220, 148
+91, 210, 142
+87, 200, 135
+82, 189, 127
+77, 177, 119
+71, 164, 110
 65, 150, 101
-58, 135, 91
-51, 118, 80
-43, 100, 67
+59, 135, 91
+52, 119, 80
+44, 101, 68
 34, 79, 53
-21, 50, 34
+22, 51, 34
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -659,28 +659,28 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-111, 257, 173
-117, 269, 181
-117, 269, 181
-115, 265, 178
-112, 259, 175
-109, 252, 170
-106, 245, 165
-102, 236, 159
-98, 227, 153
-94, 217, 146
-89, 206, 139
-85, 195, 131
+112, 257, 173
+117, 270, 182
+117, 269, 182
+115, 265, 179
+113, 260, 175
+110, 253, 170
+107, 245, 165
+103, 236, 159
+99, 227, 153
+95, 217, 146
+90, 207, 139
+85, 196, 132
 80, 184, 124
-74, 171, 115
-68, 158, 106
-62, 144, 97
-56, 129, 87
+75, 172, 116
+69, 159, 107
+63, 145, 98
+56, 130, 87
 49, 113, 76
 41, 95, 64
-32, 74, 50
-21, 49, 33
-17, 40, 27
+33, 75, 50
+22, 50, 33
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -722,30 +722,30 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-120, 276, 186
+120, 277, 187
 123, 283, 191
-122, 282, 190
-121, 278, 187
-118, 272, 183
-115, 266, 179
-112, 258, 174
-108, 250, 168
-105, 241, 162
-100, 232, 156
-96, 222, 149
-92, 211, 142
-87, 200, 135
-82, 189, 127
-77, 177, 119
-71, 164, 110
-65, 151, 101
-59, 137, 92
-52, 121, 82
-45, 105, 70
-37, 87, 58
-28, 66, 44
+123, 282, 190
+121, 278, 188
+119, 273, 184
+116, 266, 179
+112, 259, 174
+109, 250, 169
+105, 242, 163
+101, 232, 156
+97, 222, 150
+92, 212, 143
+87, 201, 135
+82, 189, 128
+77, 177, 120
+72, 165, 111
+66, 151, 102
+60, 137, 92
+53, 122, 82
+46, 105, 71
+38, 87, 59
+29, 67, 45
 18, 42, 28
-17, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -785,32 +785,32 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-124, 287, 193
-127, 293, 198
-127, 292, 197
-125, 289, 194
-123, 283, 191
-120, 277, 187
-117, 270, 182
-114, 262, 176
-110, 253, 171
-106, 245, 165
-102, 235, 158
-98, 225, 152
-93, 215, 145
-88, 204, 137
-83, 193, 130
-78, 181, 122
-73, 168, 113
-67, 155, 105
-61, 142, 95
-55, 127, 85
-48, 112, 75
+125, 287, 194
+128, 294, 198
+127, 293, 197
+126, 289, 195
+123, 284, 191
+121, 278, 187
+118, 270, 182
+114, 262, 177
+110, 254, 171
+107, 245, 165
+102, 236, 159
+98, 226, 152
+94, 215, 145
+89, 204, 138
+84, 193, 130
+79, 181, 122
+73, 169, 114
+68, 156, 105
+62, 142, 96
+55, 128, 86
+49, 112, 76
 41, 95, 64
-33, 76, 51
-24, 55, 37
-17, 40, 27
-17, 40, 27
+33, 77, 52
+24, 56, 38
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -848,34 +848,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-127, 293, 197
-131, 301, 203
-131, 301, 203
-129, 298, 201
-127, 293, 197
-124, 287, 193
-122, 280, 189
-118, 273, 184
-115, 264, 178
-111, 256, 172
-107, 247, 166
-103, 237, 160
-99, 227, 153
-94, 217, 146
-89, 206, 139
-85, 195, 131
+127, 293, 198
+131, 302, 203
+131, 302, 203
+130, 298, 201
+128, 293, 198
+125, 287, 194
+122, 281, 189
+119, 273, 184
+115, 265, 179
+111, 256, 173
+108, 247, 167
+103, 238, 160
+99, 228, 154
+95, 218, 147
+90, 207, 139
+85, 196, 132
 80, 184, 124
-74, 171, 115
+75, 172, 116
 69, 159, 107
-63, 145, 98
-57, 131, 88
-50, 116, 78
-43, 100, 67
+63, 146, 98
+57, 132, 89
+51, 117, 79
+44, 101, 68
 36, 83, 56
-27, 64, 43
-18, 42, 28
-17, 40, 27
-17, 40, 27
+28, 64, 43
+19, 43, 29
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -911,36 +911,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-126, 289, 195
-133, 307, 207
-134, 308, 207
+126, 290, 195
+134, 307, 207
+134, 309, 208
 133, 306, 206
-131, 301, 203
-128, 296, 199
-125, 289, 195
-122, 282, 190
-119, 274, 185
-115, 266, 179
-112, 257, 173
-108, 248, 167
+131, 302, 203
+129, 296, 200
+126, 290, 195
+123, 282, 190
+119, 275, 185
+116, 267, 180
+112, 258, 174
+108, 249, 168
 104, 239, 161
-99, 229, 154
-95, 219, 147
-90, 208, 140
-85, 197, 133
-80, 185, 125
-75, 173, 117
-70, 161, 108
-64, 148, 99
-58, 134, 90
-52, 120, 80
-45, 104, 70
+100, 229, 155
+95, 219, 148
+91, 208, 140
+86, 197, 133
+81, 186, 125
+76, 174, 117
+70, 161, 109
+64, 148, 100
+59, 135, 91
+52, 120, 81
+46, 105, 71
 38, 88, 59
-30, 70, 47
-21, 50, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
+31, 70, 47
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -961,7 +961,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-111, 111, 111
+112, 112, 112
 33, 33, 33
 255, 255, 255
 255, 255, 255
@@ -975,36 +975,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-134, 310, 208
-136, 313, 211
-135, 312, 210
-134, 308, 208
-132, 303, 204
-129, 297, 200
-126, 290, 195
+135, 310, 209
+136, 314, 211
+136, 312, 210
+134, 309, 208
+132, 304, 205
+129, 298, 201
+126, 291, 196
 123, 283, 191
-119, 275, 185
+120, 276, 186
 116, 267, 180
-112, 258, 174
+112, 259, 174
 108, 249, 168
-104, 239, 161
+104, 240, 162
 100, 230, 155
-95, 219, 148
-90, 209, 141
-86, 198, 133
-81, 186, 125
-76, 175, 117
-70, 162, 109
-65, 149, 101
-59, 136, 91
+96, 220, 148
+91, 209, 141
+86, 198, 134
+81, 187, 126
+76, 175, 118
+71, 163, 110
+65, 150, 101
+59, 136, 92
 53, 122, 82
-46, 107, 72
-39, 91, 61
+47, 107, 72
+40, 91, 62
 32, 74, 50
 24, 55, 37
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1025,8 +1025,8 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-90, 90, 90
-11, 11, 11
+91, 91, 91
+12, 12, 12
 0, 0, 0
 255, 255, 255
 255, 255, 255
@@ -1038,38 +1038,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-133, 307, 207
-137, 316, 213
-137, 317, 213
-136, 314, 211
-134, 310, 208
-132, 304, 205
-129, 298, 200
-126, 291, 196
-123, 283, 191
-119, 275, 185
+134, 307, 207
+138, 317, 213
+138, 317, 214
+137, 314, 212
+135, 310, 209
+132, 305, 205
+130, 298, 201
+127, 291, 196
+123, 284, 191
+120, 276, 186
 116, 267, 180
-112, 258, 174
+112, 259, 174
 108, 249, 168
-104, 239, 161
+104, 240, 162
 100, 230, 155
-95, 219, 148
+96, 220, 148
 91, 209, 141
-86, 198, 133
+86, 199, 134
 81, 187, 126
-76, 175, 118
+76, 176, 118
 71, 163, 110
-65, 150, 101
-59, 137, 92
-53, 123, 83
+66, 151, 102
+60, 138, 93
+54, 124, 83
 47, 109, 73
-40, 93, 63
-33, 76, 51
-25, 58, 39
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 63
+33, 77, 52
+26, 59, 40
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1089,7 +1089,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-29, 29, 29
+30, 30, 30
 0, 0, 0
 255, 255, 255
 255, 255, 255
@@ -1102,38 +1102,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-137, 316, 213
+138, 316, 213
+139, 320, 216
 139, 319, 215
-138, 318, 214
 137, 315, 212
-134, 310, 209
-132, 304, 205
-129, 298, 200
-126, 290, 196
-123, 283, 190
-119, 275, 185
-116, 266, 179
-112, 258, 173
-108, 248, 167
+135, 310, 209
+132, 305, 205
+130, 298, 201
+126, 291, 196
+123, 283, 191
+120, 275, 185
+116, 267, 180
+112, 258, 174
+108, 249, 168
 104, 239, 161
-99, 229, 154
-95, 219, 147
-90, 209, 140
-86, 198, 133
+100, 230, 155
+95, 220, 148
+91, 209, 141
+86, 198, 134
 81, 187, 126
 76, 175, 118
 71, 163, 110
-65, 150, 101
-59, 137, 92
-54, 124, 83
-47, 109, 74
-41, 94, 63
-34, 78, 52
-26, 61, 41
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
+66, 151, 102
+60, 138, 93
+54, 124, 84
+48, 110, 74
+41, 95, 64
+34, 79, 53
+27, 61, 41
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1165,40 +1165,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-134, 308, 207
-139, 320, 215
-139, 321, 216
-138, 319, 215
+134, 308, 208
+139, 320, 216
+140, 321, 217
+139, 319, 215
 137, 315, 212
-134, 309, 208
-132, 303, 204
+135, 310, 209
+132, 304, 205
 129, 297, 200
 126, 290, 195
-122, 282, 190
-119, 274, 184
-115, 265, 179
-111, 257, 173
-107, 247, 167
-103, 238, 160
-99, 228, 154
-95, 218, 147
-90, 208, 140
-85, 197, 133
-81, 186, 125
-76, 174, 117
-70, 162, 109
-65, 150, 101
-59, 137, 92
-53, 124, 83
-47, 110, 74
+123, 282, 190
+119, 274, 185
+116, 266, 179
+112, 257, 173
+108, 248, 167
+104, 238, 161
+99, 229, 154
+95, 219, 147
+91, 208, 140
+86, 197, 133
+81, 186, 126
+76, 175, 118
+71, 163, 110
+65, 151, 101
+60, 138, 93
+54, 124, 84
+48, 110, 74
 41, 95, 64
 34, 79, 53
-27, 62, 41
-19, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 62, 42
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1229,40 +1229,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-137, 315, 212
-139, 321, 216
-139, 321, 216
-138, 318, 214
-136, 314, 211
-134, 308, 208
-131, 302, 203
-128, 295, 199
-125, 288, 194
-122, 280, 189
-118, 272, 183
-114, 264, 178
-111, 255, 172
+137, 316, 213
+140, 322, 217
+140, 321, 217
+139, 319, 215
+137, 314, 212
+134, 309, 208
+132, 303, 204
+129, 296, 199
+125, 289, 195
+122, 281, 189
+119, 273, 184
+115, 264, 178
+111, 256, 172
 107, 246, 166
-103, 237, 159
-98, 227, 153
+103, 237, 160
+99, 227, 153
 94, 217, 146
-89, 206, 139
+90, 207, 139
 85, 196, 132
-80, 185, 124
-75, 173, 117
-70, 161, 109
-65, 149, 100
-59, 136, 92
-53, 123, 83
-47, 109, 73
-41, 94, 63
+81, 185, 125
+76, 174, 117
+70, 162, 109
+65, 150, 101
+59, 137, 92
+54, 124, 83
+48, 110, 74
+41, 95, 64
 34, 79, 53
-27, 62, 42
-19, 44, 30
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 63, 42
+19, 45, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1293,40 +1293,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-138, 317, 214
+138, 318, 214
+140, 322, 217
 139, 321, 216
-139, 320, 216
-137, 317, 213
-135, 312, 210
-133, 307, 207
-130, 300, 202
-127, 294, 198
-124, 286, 193
+138, 317, 214
+136, 313, 211
+134, 307, 207
+131, 301, 203
+128, 294, 198
+125, 287, 193
 121, 279, 188
-117, 270, 182
-114, 262, 176
-110, 253, 170
-106, 244, 164
-102, 235, 158
-98, 225, 151
-93, 215, 145
+118, 271, 183
+114, 262, 177
+110, 254, 171
+106, 245, 165
+102, 235, 159
+98, 226, 152
+94, 216, 145
 89, 205, 138
-84, 194, 131
-79, 183, 123
-74, 172, 116
-69, 160, 108
-64, 148, 99
-58, 135, 91
+85, 195, 131
+80, 184, 124
+75, 172, 116
+70, 160, 108
+64, 148, 100
+59, 136, 91
 53, 122, 82
 47, 108, 73
-40, 93, 63
-34, 78, 52
-27, 62, 41
-19, 44, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 63
+34, 79, 53
+27, 62, 42
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1356,42 +1356,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-130, 299, 201
+130, 299, 202
 138, 318, 214
-139, 320, 215
-138, 318, 214
-137, 315, 212
-135, 310, 209
-132, 305, 205
-129, 298, 201
-126, 291, 196
-123, 284, 191
-120, 276, 186
-116, 268, 181
+139, 320, 216
+139, 319, 215
+137, 315, 213
+135, 311, 209
+133, 305, 206
+130, 299, 201
+127, 292, 197
+124, 284, 192
+120, 277, 186
+117, 269, 181
 113, 260, 175
 109, 251, 169
 105, 242, 163
-101, 232, 157
+101, 233, 157
 97, 223, 150
-92, 213, 143
-88, 203, 136
-83, 192, 129
-78, 181, 122
-73, 170, 114
-68, 158, 106
-63, 146, 98
-58, 133, 90
-52, 120, 81
-46, 106, 72
+93, 213, 144
+88, 203, 137
+84, 192, 130
+79, 182, 122
+74, 170, 115
+69, 158, 107
+64, 146, 99
+58, 134, 90
+52, 121, 81
+46, 107, 72
 40, 92, 62
-33, 77, 52
-26, 60, 41
-18, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+34, 77, 52
+27, 61, 41
+19, 44, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1420,42 +1420,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-132, 304, 205
-137, 316, 213
-138, 318, 214
-137, 316, 213
-136, 312, 210
-133, 308, 207
-131, 302, 203
-128, 296, 199
-125, 289, 194
-122, 281, 189
-119, 274, 184
-115, 265, 179
-111, 257, 173
-108, 248, 167
-104, 239, 161
+132, 305, 205
+138, 317, 213
+138, 318, 215
+138, 317, 213
+136, 313, 211
+134, 308, 208
+131, 302, 204
+129, 296, 199
+126, 289, 195
+123, 282, 190
+119, 274, 185
+116, 266, 179
+112, 258, 174
+108, 249, 168
+104, 240, 162
 100, 230, 155
-95, 220, 148
-91, 210, 142
-87, 200, 135
-82, 189, 128
-77, 179, 120
-72, 167, 113
-67, 156, 105
-62, 144, 97
-57, 131, 88
-51, 118, 79
-45, 104, 70
-39, 90, 60
-32, 75, 50
-25, 59, 39
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+96, 221, 149
+92, 211, 142
+87, 201, 135
+83, 190, 128
+78, 179, 121
+73, 168, 113
+68, 156, 105
+63, 144, 97
+57, 132, 89
+52, 118, 80
+46, 105, 71
+39, 90, 61
+33, 75, 51
+26, 59, 40
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1474,7 +1474,7 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-69, 69, 69
+70, 70, 70
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1485,41 +1485,41 @@
 255, 255, 255
 255, 255, 255
 132, 304, 205
+137, 314, 212
+137, 316, 213
 136, 314, 211
-137, 315, 212
-136, 313, 211
-134, 309, 208
-132, 304, 205
-130, 299, 201
-127, 292, 197
-124, 286, 192
-121, 278, 187
-117, 270, 182
-114, 262, 177
-110, 254, 171
-106, 245, 165
-102, 236, 159
-98, 227, 153
-94, 217, 146
-90, 207, 140
-85, 197, 133
+135, 310, 209
+133, 305, 206
+130, 299, 202
+127, 293, 197
+124, 286, 193
+121, 279, 188
+118, 271, 183
+114, 263, 177
+111, 254, 171
+107, 246, 166
+103, 237, 160
+99, 227, 153
+95, 218, 147
+90, 208, 140
+86, 198, 133
 81, 187, 126
-76, 176, 118
-71, 165, 111
-66, 153, 103
+77, 176, 119
+72, 165, 111
+67, 153, 103
 61, 141, 95
-56, 128, 86
-50, 115, 78
-44, 102, 68
-38, 87, 59
-31, 72, 49
-24, 56, 38
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+56, 129, 87
+50, 116, 78
+44, 102, 69
+38, 88, 59
+32, 73, 49
+25, 57, 38
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1549,41 +1549,41 @@
 255, 255, 255
 255, 255, 255
 131, 301, 203
+135, 311, 209
+136, 312, 210
 135, 310, 209
-135, 311, 210
-134, 309, 208
 133, 306, 206
 131, 301, 203
-128, 295, 199
-125, 289, 195
-122, 282, 190
-119, 275, 185
-116, 267, 180
-112, 259, 174
+129, 296, 199
+126, 289, 195
+123, 283, 190
+120, 275, 185
+116, 268, 180
+113, 259, 175
 109, 251, 169
 105, 242, 163
 101, 233, 157
 97, 224, 151
-93, 214, 144
-88, 204, 137
-84, 194, 131
-79, 183, 123
-75, 173, 116
-70, 161, 109
+93, 215, 145
+89, 205, 138
+85, 194, 131
+80, 184, 124
+75, 173, 117
+70, 162, 109
 65, 150, 101
 60, 138, 93
-54, 125, 84
-49, 112, 75
-43, 99, 66
-36, 84, 57
-30, 69, 47
-23, 53, 36
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+55, 126, 85
+49, 113, 76
+43, 99, 67
+37, 85, 57
+30, 70, 47
+23, 54, 36
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1612,42 +1612,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-129, 296, 199
+129, 297, 200
 133, 306, 206
-133, 307, 207
-132, 305, 206
-131, 302, 203
+134, 308, 207
+133, 306, 206
+131, 302, 204
 129, 297, 200
-126, 291, 196
+127, 292, 197
 124, 285, 192
-121, 278, 187
-117, 271, 182
-114, 263, 177
-111, 255, 172
-107, 247, 166
-103, 238, 160
-99, 229, 154
-95, 220, 148
-91, 210, 142
-87, 200, 135
-82, 190, 128
-78, 180, 121
-73, 169, 114
-68, 158, 106
-63, 146, 98
-58, 134, 90
+121, 279, 188
+118, 271, 183
+115, 264, 178
+111, 256, 172
+108, 247, 167
+104, 239, 161
+100, 230, 155
+96, 220, 149
+92, 211, 142
+87, 201, 135
+83, 191, 129
+78, 180, 122
+74, 169, 114
+69, 158, 107
+64, 147, 99
+59, 135, 91
 53, 122, 82
-47, 109, 73
-41, 95, 64
-35, 81, 54
-28, 66, 44
-21, 50, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+47, 109, 74
+42, 96, 64
+35, 81, 55
+29, 66, 45
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1676,42 +1676,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-126, 289, 195
-130, 300, 202
-131, 302, 203
-130, 300, 202
+126, 290, 195
+131, 301, 203
+132, 303, 204
+131, 301, 203
 129, 297, 200
-127, 292, 197
-124, 287, 193
+127, 293, 197
+125, 287, 194
 122, 281, 189
-119, 274, 184
-116, 267, 179
-112, 259, 174
+119, 274, 185
+116, 267, 180
+113, 259, 175
 109, 251, 169
-105, 243, 163
-101, 234, 158
-98, 225, 152
-94, 216, 145
-89, 206, 139
-85, 196, 132
-81, 186, 125
-76, 176, 118
-71, 165, 111
-67, 154, 103
-62, 142, 96
-56, 130, 88
-51, 118, 79
-45, 105, 70
-39, 91, 61
-33, 77, 52
-27, 62, 41
+106, 243, 164
+102, 235, 158
+98, 226, 152
+94, 216, 146
+90, 207, 139
+86, 197, 133
+81, 187, 126
+77, 176, 119
+72, 166, 112
+67, 154, 104
+62, 143, 96
+57, 131, 88
+51, 118, 80
+46, 105, 71
+40, 92, 62
+34, 77, 52
+27, 62, 42
 20, 46, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1740,42 +1740,42 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-121, 280, 188
+122, 280, 189
 128, 294, 198
-129, 296, 199
+129, 297, 200
 128, 295, 199
-127, 292, 196
-125, 287, 193
-122, 282, 190
+127, 292, 197
+125, 288, 194
+123, 282, 190
 120, 276, 186
-117, 269, 181
-114, 262, 176
-110, 254, 171
-107, 246, 166
-103, 238, 160
+117, 270, 182
+114, 262, 177
+111, 255, 172
+107, 247, 166
+104, 239, 161
 100, 230, 155
 96, 221, 149
-92, 211, 142
+92, 212, 143
 88, 202, 136
-83, 192, 129
-79, 182, 122
-74, 171, 115
-70, 161, 108
-65, 149, 101
+84, 193, 130
+79, 182, 123
+75, 172, 116
+70, 161, 109
+65, 150, 101
 60, 138, 93
-54, 126, 85
-49, 113, 76
-43, 100, 67
-37, 87, 58
-31, 72, 49
-25, 57, 38
-18, 41, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+55, 126, 85
+49, 114, 77
+44, 101, 68
+38, 87, 59
+32, 73, 49
+25, 58, 39
+18, 42, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1805,40 +1805,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-124, 286, 193
+125, 287, 193
 126, 290, 195
-125, 289, 194
-124, 286, 192
-122, 282, 190
-120, 276, 186
-117, 270, 182
-114, 264, 178
-111, 257, 173
-108, 249, 168
-105, 241, 163
-101, 233, 157
-97, 225, 151
-94, 216, 145
-90, 207, 139
-85, 197, 133
-81, 187, 126
-77, 177, 119
-72, 167, 112
-67, 156, 105
-63, 145, 97
-58, 133, 90
-52, 121, 81
-47, 108, 73
-41, 95, 64
-35, 82, 55
-29, 67, 45
-22, 52, 35
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+126, 289, 195
+124, 286, 193
+123, 282, 190
+120, 277, 187
+118, 271, 183
+115, 264, 178
+112, 257, 173
+109, 250, 168
+105, 242, 163
+102, 234, 158
+98, 225, 152
+94, 216, 146
+90, 207, 140
+86, 198, 133
+82, 188, 127
+77, 178, 120
+73, 167, 113
+68, 156, 105
+63, 145, 98
+58, 134, 90
+53, 122, 82
+47, 109, 73
+42, 96, 65
+36, 82, 55
+29, 68, 46
+23, 53, 35
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1869,40 +1869,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
+120, 277, 187
+123, 282, 190
+123, 282, 190
+122, 280, 189
 120, 276, 186
-122, 282, 190
-122, 282, 190
-121, 279, 188
-119, 275, 185
-117, 270, 182
-115, 265, 178
-112, 258, 174
-109, 251, 169
-106, 244, 164
-102, 236, 159
-99, 228, 153
-95, 219, 148
-91, 211, 142
-87, 201, 136
-83, 192, 129
-79, 182, 123
-75, 172, 116
+118, 271, 183
+115, 265, 179
+112, 259, 174
+109, 252, 170
+106, 244, 165
+103, 237, 159
+99, 228, 154
+96, 220, 148
+92, 211, 142
+88, 202, 136
+84, 192, 130
+79, 183, 123
+75, 173, 116
 70, 162, 109
-65, 151, 101
-60, 139, 94
-55, 128, 86
-50, 116, 78
-45, 103, 69
-39, 90, 60
-33, 76, 51
-26, 62, 41
-20, 46, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+66, 151, 102
+61, 140, 94
+56, 128, 86
+51, 116, 78
+45, 104, 70
+39, 90, 61
+33, 77, 52
+27, 62, 42
+20, 47, 31
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1933,40 +1933,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-114, 264, 178
+115, 264, 178
+119, 273, 184
+119, 274, 185
 118, 273, 184
-119, 274, 184
-118, 272, 183
-116, 268, 181
-114, 264, 178
-112, 258, 174
-109, 252, 170
-106, 245, 165
-103, 238, 160
-100, 230, 155
-96, 222, 150
+117, 269, 181
+115, 264, 178
+112, 259, 174
+110, 252, 170
+107, 246, 166
+104, 238, 161
+100, 231, 155
+97, 223, 150
 93, 214, 144
 89, 205, 138
 85, 196, 132
-81, 186, 125
-76, 177, 119
-72, 166, 112
-67, 156, 105
-63, 145, 98
+81, 187, 126
+77, 177, 119
+73, 167, 112
+68, 156, 105
+63, 146, 98
 58, 134, 90
-53, 122, 82
+53, 123, 83
 48, 110, 74
-42, 97, 65
-36, 84, 56
-30, 70, 47
-24, 55, 37
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+42, 98, 66
+37, 84, 57
+31, 71, 48
+24, 56, 38
 17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -1997,40 +1997,40 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-105, 243, 163
-113, 262, 176
-115, 265, 178
-114, 264, 178
-113, 261, 176
-111, 256, 173
-109, 251, 169
-106, 245, 165
-103, 239, 161
-100, 231, 156
-97, 224, 151
-94, 216, 145
-90, 207, 140
-86, 199, 134
-82, 190, 128
-78, 180, 121
-74, 170, 115
-69, 160, 108
+106, 243, 164
+114, 262, 177
+115, 265, 179
+115, 264, 178
+114, 261, 176
+112, 257, 173
+109, 252, 170
+107, 246, 166
+104, 239, 161
+101, 232, 156
+98, 224, 151
+94, 216, 146
+90, 208, 140
+87, 199, 134
+83, 190, 128
+79, 181, 122
+74, 171, 115
+70, 161, 108
 65, 150, 101
-60, 139, 93
-55, 128, 86
-50, 116, 78
+61, 139, 94
+56, 128, 86
+51, 116, 78
 45, 104, 70
-39, 91, 61
-33, 77, 52
-27, 63, 42
-21, 48, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+40, 91, 62
+34, 78, 52
+28, 64, 43
+21, 49, 33
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2062,38 +2062,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-107, 247, 167
-110, 254, 171
-110, 254, 171
-109, 252, 170
 108, 248, 167
+111, 254, 171
+111, 255, 172
+110, 253, 170
+108, 249, 168
 106, 244, 164
-103, 238, 160
-100, 231, 156
-97, 224, 151
-94, 217, 146
-91, 209, 141
-87, 201, 135
-83, 192, 129
-79, 183, 123
-75, 174, 117
-71, 164, 110
-67, 154, 103
-62, 143, 96
-57, 132, 89
-52, 121, 81
-47, 109, 73
+104, 238, 161
+101, 232, 156
+98, 225, 152
+95, 217, 147
+91, 210, 141
+87, 201, 136
+84, 193, 130
+80, 183, 124
+76, 174, 117
+71, 164, 111
+67, 154, 104
+62, 144, 97
+58, 133, 89
+53, 121, 82
+48, 109, 74
 42, 97, 65
-36, 84, 56
-30, 70, 47
-24, 56, 37
-17, 41, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+37, 84, 57
+31, 71, 48
+24, 56, 38
+18, 41, 28
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2126,38 +2126,38 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-97, 224, 151
-104, 241, 162
+98, 224, 151
+105, 241, 163
+106, 244, 164
 106, 243, 164
-105, 242, 163
-104, 239, 161
-102, 235, 158
+104, 240, 162
+102, 235, 159
 100, 230, 155
-97, 223, 150
+97, 224, 151
 94, 217, 146
-91, 209, 141
-87, 202, 136
-84, 193, 130
-80, 185, 124
-76, 176, 118
-72, 166, 112
-68, 157, 105
-63, 146, 99
-59, 136, 91
-54, 125, 84
-49, 113, 76
-44, 101, 68
-38, 89, 60
+91, 210, 141
+88, 202, 136
+84, 194, 131
+81, 185, 125
+77, 176, 119
+73, 167, 112
+68, 157, 106
+64, 147, 99
+59, 136, 92
+55, 125, 84
+50, 114, 77
+44, 102, 69
+39, 89, 60
 33, 76, 51
-27, 62, 42
-20, 47, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 63, 42
+21, 48, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2191,36 +2191,36 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-96, 222, 150
-100, 230, 155
+97, 223, 150
 100, 231, 156
-99, 229, 154
-98, 225, 152
-96, 220, 148
-93, 215, 144
-90, 208, 140
-87, 201, 135
-84, 193, 130
-80, 185, 125
-77, 177, 119
+101, 232, 156
+100, 230, 155
+98, 226, 152
+96, 221, 149
+94, 215, 145
+91, 209, 141
+88, 202, 136
+84, 194, 131
+81, 186, 125
+77, 177, 120
 73, 168, 113
 69, 159, 107
-64, 149, 100
-60, 139, 93
-55, 128, 86
+65, 149, 101
+60, 139, 94
+56, 128, 87
 51, 117, 79
-46, 105, 71
-40, 93, 63
-35, 81, 54
-29, 67, 45
-23, 53, 36
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+46, 106, 71
+41, 94, 63
+35, 81, 55
+29, 68, 46
+23, 54, 36
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2256,34 +2256,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-92, 213, 143
-94, 217, 146
-94, 217, 146
-93, 214, 144
-91, 210, 141
+93, 213, 144
+95, 218, 147
+95, 218, 147
+93, 215, 145
+92, 211, 142
 89, 205, 138
-86, 199, 134
-83, 192, 129
-80, 184, 124
-76, 177, 119
-73, 168, 113
-69, 159, 107
+87, 199, 134
+84, 192, 130
+80, 185, 125
+77, 177, 119
+73, 169, 114
+69, 160, 108
 65, 150, 101
-61, 140, 94
-56, 130, 87
-52, 119, 80
-47, 108, 73
-42, 96, 65
-36, 84, 57
-31, 71, 48
+61, 141, 95
+57, 130, 88
+52, 120, 81
+47, 109, 73
+42, 97, 65
+37, 85, 57
+31, 72, 48
 25, 58, 39
-18, 43, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+19, 44, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2320,34 +2320,34 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-75, 173, 117
-86, 199, 134
-88, 203, 136
-87, 202, 136
-86, 198, 134
-84, 194, 130
-82, 188, 127
-79, 182, 122
 76, 174, 117
-72, 167, 112
-69, 158, 107
+87, 200, 135
+88, 203, 137
+88, 202, 136
+86, 199, 134
+84, 194, 131
+82, 189, 127
+79, 182, 123
+76, 175, 118
+73, 167, 113
+69, 159, 107
 65, 150, 101
-61, 140, 94
+61, 141, 95
 57, 131, 88
-52, 120, 81
-47, 110, 74
-42, 98, 66
-37, 86, 58
+53, 121, 81
+48, 110, 74
+43, 99, 67
+38, 87, 59
 32, 74, 50
-26, 61, 41
-20, 47, 31
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+27, 61, 41
+21, 47, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2385,32 +2385,32 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-71, 165, 111
-79, 183, 123
-80, 186, 125
+72, 165, 111
 80, 184, 124
-78, 181, 122
-76, 176, 118
-74, 170, 114
-71, 163, 110
-67, 156, 105
-64, 148, 99
-60, 139, 93
-56, 130, 87
+81, 186, 126
+80, 185, 125
+79, 181, 122
+77, 176, 119
+74, 170, 115
+71, 164, 110
+68, 156, 105
+64, 148, 100
+61, 139, 94
+57, 130, 88
 52, 120, 81
-47, 110, 74
-43, 99, 66
-38, 87, 59
-32, 75, 51
-27, 62, 42
+48, 110, 74
+43, 99, 67
+38, 88, 59
+33, 76, 51
+27, 63, 42
 21, 49, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2451,28 +2451,28 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-71, 164, 110
-72, 167, 112
-72, 165, 111
+71, 164, 111
+73, 167, 113
+72, 166, 112
 70, 162, 109
-68, 156, 105
-65, 150, 101
-62, 143, 96
-59, 135, 91
-55, 127, 85
-51, 118, 79
+68, 157, 106
+66, 151, 102
+62, 144, 97
+59, 136, 91
+55, 127, 86
+51, 118, 80
 47, 108, 73
-42, 98, 66
-37, 87, 58
-32, 75, 50
-27, 62, 42
-21, 49, 33
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+43, 98, 66
+38, 87, 59
+33, 75, 51
+27, 63, 42
+22, 50, 34
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2516,26 +2516,26 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-60, 140, 94
-62, 144, 97
-62, 143, 96
-60, 140, 94
-58, 135, 91
-55, 128, 86
-52, 121, 81
+61, 140, 94
+63, 145, 98
+63, 144, 97
+61, 140, 95
+59, 135, 91
+56, 129, 87
+53, 121, 82
 49, 113, 76
 45, 104, 70
-41, 94, 63
-36, 83, 56
-31, 72, 49
-26, 60, 40
-20, 47, 32
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+41, 94, 64
+37, 84, 57
+32, 73, 49
+26, 61, 41
+21, 48, 32
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2582,22 +2582,22 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-50, 116, 78
 51, 117, 79
-49, 114, 77
-47, 109, 74
-44, 103, 69
-41, 95, 64
-37, 87, 58
-33, 77, 52
-29, 66, 45
+51, 118, 79
+50, 115, 77
+48, 110, 74
+45, 103, 70
+42, 96, 65
+38, 87, 59
+34, 77, 52
+29, 67, 45
 24, 55, 37
-18, 42, 28
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+19, 43, 29
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2648,18 +2648,18 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-35, 82, 55
-35, 82, 55
-34, 78, 53
-31, 72, 48
+36, 82, 55
+36, 83, 56
+34, 79, 53
+31, 72, 49
 28, 64, 43
-23, 55, 37
-19, 44, 29
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+24, 55, 37
+19, 44, 30
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
@@ -2716,10 +2716,10 @@
 255, 255, 255
 255, 255, 255
 255, 255, 255
-17, 40, 27
-17, 40, 27
-17, 40, 27
-17, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
+18, 40, 27
 255, 255, 255
 255, 255, 255
 255, 255, 255
diff --git a/examples/pos/withstatement.effekt b/examples/pos/withstatement.effekt
index 74eaefec..c2cbdd2f 100644
--- a/examples/pos/withstatement.effekt
+++ b/examples/pos/withstatement.effekt
@@ -11,8 +11,8 @@ def bar { f: (Int, String) => Unit / {}}: Unit =
 
 def user(): Unit = {
     with printer;
-    with x: Int = foreach([1,2,3]);
-    with (a, b) = bar;
+    with val x: Int = foreach([1,2,3]);
+    with val (a, b) = bar;
     println(show(x) ++ b);
     val z = {
         val x = 2;
diff --git a/examples/stdlib/string/strings.effekt b/examples/stdlib/string/strings.effekt
index a121c490..9d0ed00e 100644
--- a/examples/stdlib/string/strings.effekt
+++ b/examples/stdlib/string/strings.effekt
@@ -18,7 +18,7 @@ def main() = {
   charOrOutOfBounds { str.charAt(10) }    // 100
 
   println(utf8ByteCount('a'))      // 1
-  println(utf8ByteCount(\u{0a00})) // 3
+  println(utf8ByteCount(\u0a00)) // 3
 
 
   charOrOutOfBounds { str.charAt(11) }
@@ -67,4 +67,4 @@ def main() = {
   println("foobarbaz".lastIndexOf("a"))
   println("foobarba".lastIndexOf("a"))
 
-}
\ No newline at end of file
+}
diff --git a/examples/stdlib/string/unicode.effekt b/examples/stdlib/string/unicode.effekt
index 440104e4..884ae6e0 100644
--- a/examples/stdlib/string/unicode.effekt
+++ b/examples/stdlib/string/unicode.effekt
@@ -23,7 +23,7 @@ def main() = {
   println("f😅o😅o".indexOf("😅", 0))    // Some(1)
 
   println("f😅o😅o".split("😅"))
-  println(toString(\u{1FAD6}))
+  println(toString(\u1FAD6))
   println(toString('🫖'))
   println(toString('貫'))
 
diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt
index 0a962588..bc454ab7 100644
--- a/libraries/common/effekt.effekt
+++ b/libraries/common/effekt.effekt
@@ -375,23 +375,9 @@ extern pure def sqrt(x: Double): Double =
   js "Math.sqrt(${x})"
   chez "(sqrt ${x})"
   ml "Math.sqrt ${x}"
-  llvm "%z = call %Double @llvm.sqrt.f64(double %x) ret %Double %z"
-
-extern llvm """
-declare double @llvm.sqrt.f64(double %x)
-"""
 
 def square(x: Double): Double = x * x
 
-def neg(n: Double): Double =
-  0.0 - n
-
-def max(n: Double, m: Double): Double =
-  if (n > m) n else m
-
-def abs(n: Double): Double =
-  max(n, neg(n))
-
 extern pure def log(x: Double): Double =
   js "Math.log(${x})"
   chez "(log ${x})"
@@ -415,22 +401,22 @@ extern pure def _pi(): Double =
 
 val PI: Double = _pi()
 
+extern pure def toByte(n: Int): Byte =
+  llvm """
+    %res = trunc i64 %n to i8
+    ret i8 %res
+  """
+  js "${n}"
+
 extern pure def toInt(d: Double): Int =
-  js "Math.trunc(${d})"
-  chez "(flonum->fixnum ${d})"
-  ml "Real.trunc ${d}"
-  llvm "%z = fptosi double %d to %Int ret %Int %z"
+  js "Math.round(${d})"
+  chez "(round ${d})"
+  ml "Real.round ${d}"
 
 extern pure def toDouble(d: Int): Double =
   js "${d}"
   chez "${d}"
   ml "Real.fromInt ${d}"
-  llvm "%z = sitofp i64 %d to double ret double %z"
-
-extern pure def round(d: Double): Int =
-  js "Math.round(${d})"
-  chez "(round ${d})"
-  ml "Real.round ${d}"
 
 extern pure def floor(d: Double): Int =
   js "Math.floor(${d})"
@@ -513,12 +499,6 @@ extern pure def infixLt(x: Double, y: Double): Bool =
   js "(${x} < ${y})"
   chez "(< ${x} ${y})"
   ml "(${x}: real) < ${y}"
-  llvm """
-    %z = fcmp olt %Double %x, %y
-    %fat_z = zext i1 %z to i64
-    %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
-    ret %Pos %adt_boolean
-  """
 
 extern pure def infixLte(x: Double, y: Double): Bool =
   js "(${x} <= ${y})"
@@ -529,12 +509,6 @@ extern pure def infixGt(x: Double, y: Double): Bool =
   js "(${x} > ${y})"
   chez "(> ${x} ${y})"
   ml "(${x}: real) > ${y}"
-  llvm """
-    %z = fcmp ogt %Double %x, %y
-    %fat_z = zext i1 %z to i64
-    %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
-    ret %Pos %adt_boolean
-  """
 
 extern pure def infixGte(x: Double, y: Double): Bool =
   js "(${x} >= ${y})"
@@ -583,32 +557,6 @@ def infixAnd { first: => Bool } { second: => Bool }: Bool =
   if (first()) second() else false
 
 
-// Bitwise operations
-// ===============
-extern pure def bitwiseShl(x: Int, y: Int): Int =
-  js "(${x} << ${y})"
-  llvm "%z = shl %Int %x, %y ret %Int %z"
-
-extern pure def bitwiseAnd(x: Int, y: Int): Int =
-  js "(${x} & ${y})"
-  llvm "%z = and %Int %x, %y ret %Int %z"
-
-extern pure def bitwiseXor(x: Int, y: Int): Int =
-  js "(${x} ^ ${y})"
-  llvm "%z = xor %Int %x, %y ret %Int %z"
-
-
-// Byte operations
-// ===============
-extern pure def toByte(n: Int): Byte =
-  js "${n}"
-  llvm "%z = trunc %Int %n to %Byte ret %Byte %z"
-
-extern pure def toInt(n: Byte): Int =
-  js "${n}"
-  llvm "%z = zext %Byte %n to %Int ret %Int %z"
-
-
 // Undefined and Null
 // ==================
 // Should only be used for interop with FFI, since values in Effekt
diff --git a/libraries/common/list.effekt b/libraries/common/list.effekt
index a41fc52e..cbd75616 100644
--- a/libraries/common/list.effekt
+++ b/libraries/common/list.effekt
@@ -128,6 +128,8 @@ def foreach[A](l: List[A]) { f: A => Unit / Control } : Unit = {
   }
 }
 
+
+
 // Traverse a list, applying the given action on every element and its (zero-based) index.
 //
 // O(N)
diff --git a/libraries/common/string.effekt b/libraries/common/string.effekt
index 73c23907..1162f861 100644
--- a/libraries/common/string.effekt
+++ b/libraries/common/string.effekt
@@ -305,16 +305,16 @@ extern type StringIterator
  * Also see: https://en.wikipedia.org/wiki/UTF-8
  */
 def utf8ByteCount(codepoint: Char): Int = codepoint match {
-  case c and c >= \u{0000}  and c <= \u{007F}   => 1
-  case c and c >= \u{0080}  and c <= \u{07FF}   => 2
-  case c and c >= \u{0800}  and c <= \u{FFFF}   => 3
-  case c and c >= \u{10000} and c <= \u{10FFFF} => 4
+  case c and c >= \u0000  and c <= \u007F   => 1
+  case c and c >= \u0080  and c <= \u07FF   => 2
+  case c and c >= \u0800  and c <= \uFFFF   => 3
+  case c and c >= \u10000 and c <= \u10FFFF => 4
   case c => panic("Not a valid code point")
 }
 
 def utf16UnitCount(codepoint: Char): Int = codepoint match {
-  case c and c >= \u{0000}  and c <= \u{FFFF}   => 1
-  case c and c >= \u{10000} and c <= \u{10FFFF} => 4
+  case c and c >= \u0000  and c <= \uFFFF   => 1
+  case c and c >= \u10000 and c <= \u10FFFF => 4
   case c => panic("Not a valid code point")
 }
 


Test patch:
diff --git a/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala b/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala
index fd8fb750..ae0ee1bc 100644
--- a/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala
+++ b/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala
@@ -18,6 +18,7 @@ abstract class ChezSchemeTests extends EffektTests {
 
   // Test files which are to be ignored (since features are missing or known bugs exist)
   override def ignored: List[File] = List(
+
     examplesDir / "llvm",
 
     examplesDir / "ml",
@@ -78,7 +79,6 @@ class ChezSchemeLiftTests extends ChezSchemeTests {
   def backendName = "chez-lift"
 
   override def ignored: List[File] = super.ignored ++ List(
-
     // regions are not yet supported
     examplesDir / "benchmarks" / "generator.effekt",
     examplesDir / "pos" / "capture" / "regions.effekt",
diff --git a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala
index e0afb725..aad4eb8e 100644
--- a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala
+++ b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala
@@ -107,9 +107,6 @@ class LLVMTests extends EffektTests {
 
     // Generic equality
     examplesDir / "pos" / "issue429.effekt",
-
-    // Math operations
-    examplesDir / "pos" / "doubles.effekt",
   )
 
   override lazy val ignored: List[File] = bugs ++ missingFeatures
diff --git a/effekt/jvm/src/test/scala/effekt/LexerTests.scala b/effekt/jvm/src/test/scala/effekt/LexerTests.scala
new file mode 100644
index 00000000..b840cdf1
--- /dev/null
+++ b/effekt/jvm/src/test/scala/effekt/LexerTests.scala
@@ -0,0 +1,245 @@
+package effekt
+
+import effekt.lexer.TokenKind.*
+import effekt.lexer.{Lexer, Token, TokenKind, LexerError }
+import effekt.lexer.LexerError.*
+
+import kiama.util.{ StringSource }
+
+import munit.Location
+
+class LexerTests extends munit.FunSuite {
+
+  def assertTokensEq(prog: String, expected: TokenKind*)(using Location): Unit = {
+    val tokens = Lexer(StringSource(prog, "")).run()
+    tokens.zipAll(expected, null, null).foreach((t1, t2) => assertEquals(t1.kind, t2))
+  }
+
+  def assertSuccess(prog: String)(using Location): Unit =
+    try {
+      Lexer(StringSource(prog, "")).run()
+    } catch {
+        case LexerError(msg, _, _) => fail(msg)
+    }
+
+  def assertFailure(prog: String)(using Location): Unit =
+    try {
+      Lexer(StringSource(prog, "")).run()
+      fail(s"expected an lexer error but found none")
+    } catch {
+      case LexerError(_, _, _) => ()
+    }
+
+  test("function definition") {
+    val prog =
+      """def f[A](x: A, y: A): () => (A, A) at {} =
+        |    return box { () => (x, y) }
+        |""".stripMargin
+    assertTokensEq(
+      prog,
+      `def`, Ident("f"), `[`, Ident("A"), `]`,
+      `(`, Ident("x"), `:`, Ident("A"), `,`, Ident("y"), `:`, Ident("A"), `)`,
+      `:`, `(`, `)`, `=>`, `(`, Ident("A"), `,`, Ident("A"), `)`, `at`, `{`, `}`, `=`, Newline,
+      `return`, `box`, `{`, `(`, `)`, `=>`, `(`, Ident("x"), `,`, Ident("y"), `)`, `}`, Newline,
+      EOF
+    )
+  }
+
+  test("numbers") {
+    val num = "12.34 100 200 123.345 1 -12.34 -100 -123.345 -1"
+    assertTokensEq(
+      num,
+      Float(12.34), Integer(100), Integer(200), Float(123.345), Integer(1),
+      Float(-12.34), Integer(-100), Float(-123.345), Integer(-1),
+      EOF
+    )
+  }
+
+  test("big numbers") {
+    assertFailure("9223372036854775808")
+    assertTokensEq("9223372036854775807", Integer(9223372036854775807L), EOF)
+    assertFailure("-9223372036854775809")
+    assertTokensEq("-9223372036854775808", Integer(-9223372036854775808L), EOF)
+    // the max double value is 1.7976931348623157E308, without "e notation/scientific notation" support in the lexer,
+    // we refrain from writing it down in full length here
+  }
+
+  test("symbols") {
+    val prog = "=> <= < >= > / * - && ++ +"
+    assertTokensEq(
+      prog,
+      `=>`, `<=`, `<`, `>=`, `>`, `/`, `*`, `-`, `&&`, `++`, `+`, EOF
+    )
+  }
+
+  test("single line strings") {
+    val prog = """ "hello, world" "" """
+    assertTokensEq(
+      prog,
+      Str("hello, world", false),
+      Str("", false),
+      EOF
+    )
+    assertTokensEq("\"\\\"hello\\\"\"", Str("\"hello\"", false), EOF)
+    assertTokensEq("\" \\n\"", Str(" \n", false), EOF)
+    assertTokensEq("\" \\t \"", Str(" \t ", false), EOF)
+    assertFailure("\"\\k\"")
+    assertTokensEq("\"\\u001b\"", Str("\u001b", false), EOF)
+  }
+
+  test("characters") {
+    assertTokensEq("' '", Chr(' '), EOF)
+    assertTokensEq("'😅'", Chr(0x1F605), EOF)
+    assertTokensEq("'\\'", Chr('\\'), EOF)
+    assertTokensEq("'\n'", Chr('\n'), EOF)
+    assertTokensEq("'\t'", Chr('\t'), EOF)
+    assertTokensEq("\\u0000", Chr(0), EOF)
+    assertTokensEq("\\uFFFF", Chr(0xFFFF), EOF)
+    assertTokensEq("\\u10FFFF{ val", Chr(0x10FFFF), `{`, `val`, EOF)
+    assertTokensEq("val s =\\u0000+ 1", `val`, Ident("s"), `=`, Chr(0), `+`, Integer(1), EOF)
+    assertFailure("''")
+  }
+
+  test("multi line strings") {
+    val prog =
+      "\"\"\"val def interface \"\" \"\n" +
+      "continues here \t \r\n" +
+      " and is end\n" +
+      " \"\"\""
+    assertTokensEq(
+      prog,
+      Str("val def interface \"\" \"\ncontinues here \t \r\n and is end\n ", true),
+      EOF
+    )
+    assertTokensEq("\" \"\"\"", Str(" ", false), Str("", false), EOF)
+    assertFailure("\"\"\"")
+    assertFailure("\"\"\" \"")
+  }
+
+  test("quoted single-line string") {
+    val prog = """ "this is a quote ${ xs.map { x => " ${x + 1}" } } after the quote" """
+    assertTokensEq(
+      prog,
+      Str("this is a quote ",false),
+      `${`, Ident("xs"), `.`, Ident("map"),
+      `{`, Ident("x"), `=>`, Str(" ",false),
+      `${`, Ident("x"), `+`, Integer(1), TokenKind.`}`,
+      Str("",false), TokenKind.`}`, TokenKind.`}`,
+      Str(" after the quote",false),
+      EOF
+    )
+  }
+
+  test("interpolated string") {
+    val prog = """ "${x + 1}${x + 2}" """
+    assertTokensEq(
+      prog,
+      Str("", false),
+      `${`, Ident("x"), `+`, Integer(1), TokenKind.`}`,
+      Str("", false),
+      `${`, Ident("x"), `+`, Integer(2), TokenKind.`}`,
+      Str("", false),
+      EOF
+    )
+  }
+
+  test("quoted multi-line string") {
+    val prog =
+      "\"\"\"multi-line quote\n" +
+      "${x + 1}, ${y + 1} \n" +
+      "\"\"\""
+    assertTokensEq(
+      prog,
+      Str("multi-line quote\n", true),
+      `${`, Ident("x"), `+`, Integer(1), `}`, Str(", ", true), `${`, Ident("y"), `+`, Integer(1), `}`, Str(" \n", true),
+      EOF
+    )
+  }
+
+  test("simple nested interpolated string") {
+    val prog = "\"${\"${1}\"2}\""
+    assertSuccess(prog)
+  }
+
+  test("singleline comment") {
+    val prog =
+      """interface Eff { def operation(): Unit }
+        |// val x = 1 def while / \t still\n comment
+        |val x = 2""".stripMargin
+    assertTokensEq(
+      prog,
+      `interface`, Ident("Eff"), `{`, `def`, Ident("operation"), `(`, `)`, `:`, Ident("Unit"), `}`, Newline,
+      Comment(" val x = 1 def while / \\t still\\n comment"), Newline,
+      `val`, Ident("x"), `=`, Integer(2),
+      EOF
+    )
+  }
+
+  test("multiline comment") {
+    val prog =
+      """val x = 42
+        |/* comment begin def return /* * /
+        |still comment
+        |here's the end
+        |*/
+        |def main(): Unit = ()""".stripMargin
+
+    assertTokensEq(
+      prog,
+      `val`, Ident("x"), `=`, Integer(42), Newline,
+      Comment(" comment begin def return /* * /\nstill comment\nhere's the end\n"), Newline,
+      `def`, Ident("main"), `(`, `)`, `:`, Ident("Unit"), `=`, `(`, `)`,
+      EOF
+    )
+  }
+
+  test("newline") {
+    val prog =
+      """ val next = f() // Comment
+      |g()
+      |""".stripMargin
+    assertTokensEq(
+      prog,
+      `val`, Ident("next"), `=`, Ident("f"), `(`, `)`, Comment(" Comment"), Newline,
+      Ident("g"), `(`, `)`, Newline,
+      EOF
+    )
+  }
+
+  test("empty") {
+    val prog = ""
+    assertTokensEq(prog, EOF)
+  }
+
+  test("ignore whitespace") {
+    val prog =
+      """// interface definition
+        |  interface
+        |
+        |     Eff[A,
+        |  B] { def operation
+        |  [C]
+        |  (x:    C)  :
+        |
+        |  (A, B)
+        |
+        |  }
+        |""".stripMargin
+    assertTokensEq(
+      prog,
+      Comment(" interface definition"), Newline,
+      `interface`, Newline, Newline, Ident("Eff"), `[`, Ident("A"), `,`, Newline, Ident("B"), `]`, `{`,
+      `def`, Ident("operation"), Newline, `[`, Ident("C"), `]`, Newline, `(`, Ident("x"), `:`, Ident("C"), `)`, `:`, Newline, Newline, `(`, Ident("A"), `,`, Ident("B"), `)`, Newline,
+      Newline,
+      `}`, Newline,
+      EOF
+    )
+  }
+
+  test("big file") {
+    // val start = System.nanoTime()
+    val file = scala.io.Source.fromFile("libraries/common/list.effekt").mkString
+    assertSuccess(file)
+    // println((System.nanoTime() - start) * 1e-9)
+  }
+}
diff --git a/effekt/jvm/src/test/scala/effekt/RecursiveDescentTests.scala b/effekt/jvm/src/test/scala/effekt/RecursiveDescentTests.scala
new file mode 100644
index 00000000..e6f97315
--- /dev/null
+++ b/effekt/jvm/src/test/scala/effekt/RecursiveDescentTests.scala
@@ -0,0 +1,637 @@
+package effekt
+
+import effekt.source.*
+
+import effekt.lexer.{ Lexer, Token, TokenKind }
+import effekt.lexer.TokenKind.*
+
+import kiama.util.{ Positions, StringSource }
+
+import munit.Location
+
+class RecursiveDescentTests extends munit.FunSuite {
+
+  def parser(input: String, positions: Positions)(using munit.Location): RecursiveDescent = {
+    val source = StringSource(input, "")
+    val lexer = effekt.lexer.Lexer(source)
+    val tokens = lexer.run()
+    // TODO catch LexerError exception?
+    new RecursiveDescent(positions, tokens, source)
+  }
+
+  def parse[R](input: String, f: RecursiveDescent => R, positions: Positions = new Positions())(using munit.Location): R =
+    try {
+      val p = parser(input, positions)
+      val result = f(p)
+      assert(p.peek(TokenKind.EOF), s"Did not consume everything: ${p.peek}")
+      result
+    } catch {
+      case Fail(msg, pos) =>
+        fail(s"Unexpected parse error (token index ${pos}): ${msg}")
+    }
+
+  def parseExpr(input: String, positions: Positions = new Positions())(using munit.Location): Term =
+    parse(input, _.expr())
+
+  def parseStmt(input: String, positions: Positions = new Positions())(using munit.Location): Stmt =
+    parse(input, _.stmt())
+
+  def parseStmts(input: String, positions: Positions = new Positions())(using munit.Location): Stmt =
+    parse(input, _.stmts())
+
+  def parseMatchPattern(input: String, positions: Positions = new Positions())(using munit.Location): MatchPattern =
+    parse(input, _.matchPattern())
+
+  def parseMatchClause(input: String, positions: Positions = new Positions())(using munit.Location): MatchClause =
+    parse(input, _.matchClause())
+
+  def parseValueType(input: String, positions: Positions = new Positions())(using munit.Location): ValueType =
+    parse(input, _.valueType())
+
+  def parseBlockType(input: String, positions: Positions = new Positions())(using munit.Location): BlockType =
+    parse(input, _.blockType())
+
+  def parseOpClause(input: String, positions: Positions = new Positions())(using munit.Location): OpClause =
+    parse(input, _.opClause())
+
+  def parseImplementation(input: String, positions: Positions = new Positions())(using munit.Location): Implementation =
+    parse(input, _.implementation())
+
+  def parseTry(input: String, positions: Positions = new Positions())(using munit.Location): Term =
+    parse(input, _.tryExpr())
+
+  def parseParams(input: String, positions: Positions = new Positions())(using munit.Location): (List[Id], List[ValueParam], List[BlockParam]) =
+    parse(input, _.params())
+
+  def parseLambdaParams(input: String, positions: Positions = new Positions())(using munit.Location): (List[Id], List[ValueParam], List[BlockParam]) =
+    parse(input, _.lambdaParams())
+
+  def parseDefinition(input: String, positions: Positions = new Positions())(using munit.Location): Def =
+    parse(input, _.definition())
+
+  def parseDefinitions(input: String, positions: Positions = new Positions())(using munit.Location): List[Def] =
+    parse(input, _.definitions())
+
+  def parseToplevel(input: String, positions: Positions = new Positions())(using munit.Location): Def =
+    parse(input, _.toplevel())
+
+  def parseProgram(input: String, positions: Positions = new Positions())(using munit.Location): ModuleDecl =
+    parse(input, _.program())
+
+  def parseExternDef(input: String, positions: Positions = new Positions())(using munit.Location): Def =
+    parse(input, _.externDef())
+
+  test("Peeking") {
+    implicit def toToken(t: TokenKind): Token = Token(0, 0, t)
+    def peek(tokens: Seq[Token], offset: Int): Token =
+      new RecursiveDescent(new Positions, tokens, StringSource("", "test")).peek(offset)
+
+    val tokens = List[Token](`(`, Space, Newline, `)`, Space, `=>`, EOF)
+    assertEquals(peek(tokens, 0).kind, `(`)
+    assertEquals(peek(tokens, 1).kind, `)`)
+    assertEquals(peek(tokens, 2).kind, `=>`)
+    assertEquals(peek(tokens, 3).kind, EOF)
+  }
+
+  test("Simple expressions") {
+    parseExpr("42")
+    parseExpr("f")
+    parseExpr("f(a)")
+    parseExpr("f(a, 42)")
+    parseExpr("\\u0000")
+
+    parseExpr("l.foreach { _ => 42 }")
+
+    assertEquals(
+      parseExpr("loop { f }"),
+      Call(IdTarget(IdRef(Nil, "loop")), Nil, Nil, List(Var(IdRef(Nil, "f")))))
+
+    assertNotEquals(
+      parseExpr("f.m(a, 42)"),
+      parseExpr("(f.m)(a, 42)"))
+
+    assertEquals(
+      parseExpr("f(a, 42)()"),
+      parseExpr("(f(a, 42))()"))
+
+    assertEquals(
+      parseExpr("foo.bar"),
+      // At the moment uniform function call syntax is always a method call
+      MethodCall(Var(IdRef(Nil, "foo")), IdRef(Nil, "bar"),Nil, Nil, Nil))
+
+    parseExpr("resume(42)")
+    parseExpr("in(42)")
+
+    parseExpr("fun() { foo(()) }")
+
+    parseExpr("10.seconds")
+  }
+
+  test("Boxing") {
+    parseExpr("box f")
+    parseExpr("unbox f")
+    assertEquals(
+      parseExpr("unbox box f"),
+      parseExpr("unbox (box f)")
+    )
+    assertNotEquals(
+      parseExpr("box { 42 }"),
+      parseExpr("box {} { 42 }")
+    )
+    parseExpr("box { (x: Int) => x }")
+    parseExpr("box new Fresh { def fresh() = \"42\" }")
+
+    // { f } is parsed as a capture set and not backtracked.
+    intercept[Throwable] { parseExpr("box { f }") }
+  }
+
+  test("Pattern matching") {
+    parseExpr(
+      """do raise(RuntimeError(), msg) match {}
+        |""".stripMargin)
+
+    parseExpr(
+      """x match {
+        |  case 0 => 42
+        |  case _ => <{ "Test" }>
+        |}
+        |""".stripMargin)
+
+    parseExpr(
+      """x match {
+        |  case Some(b) => acc = Cons(b, acc)
+        |}
+        |""".stripMargin)
+  }
+
+  test("Qualified names") {
+    assertEquals(parseExpr("map"), Var(IdRef(List(), "map")))
+    assertEquals(parseExpr("list::map"), Var(IdRef(List("list"), "map")))
+    assertEquals(parseExpr("list::internal::test"), Var(IdRef(List("list", "internal"), "test")))
+  }
+
+  test("Operator precedence") {
+    parseExpr("1 + 1")
+
+    assertEquals(
+      parseExpr("1 + 2 + 3"),
+      parseExpr("(1 + 2) + 3"))
+
+    assertEquals(
+      parseExpr("1 + 2 * 3"),
+      parseExpr("1 + (2 * 3)"))
+
+    assertEquals(
+      parseExpr("1 + 2 * 3 == 4 + 5"),
+      parseExpr("(1 + (2 * 3)) == (4 + 5)"))
+
+    parseExpr("i = i + 1")
+
+    parseExpr("compare(x, y) && go(next)")
+
+    parseExpr("foo || bar")
+  }
+
+  test("Dangling else") {
+    assertEquals(
+      parseExpr("if (42) if (42) x else y"),
+      parseExpr("if (42) (if (42) x else y) else ()"))
+  }
+
+  test("Simple statements") {
+    parseStmt("42")
+    parseStmt("return 42")
+    parseStmt("{ f(); return 43 }")
+    parseStmt("{ f(); 43 }")
+  }
+
+  test("Compound statements") {
+    parseStmts(
+      """ val x = { 42; 43 };
+        | val y = f(x);
+        | y
+        |""".stripMargin)
+
+    parseStmts(
+      """with foo().baz;
+        |bar()
+        |""".stripMargin)
+
+    parseStmts(
+      """var x = baz;
+        |return x
+        |""".stripMargin)
+
+    parseStmts(
+      """var x in r = baz;
+        |return x
+        |""".stripMargin)
+
+    // trailing semicolon
+    parseStmts(
+      """return x;
+        |""".stripMargin)
+
+    parseStmts("fun() { x = x + 1; x }")
+  }
+
+  test("Definition statements") {
+    parseStmts(
+      """val x = 42
+        |type T = Int
+        |()
+        |""".stripMargin)
+
+    parseStmts("val (left, right) = list; return left")
+  }
+
+  test("Semicolon insertion") {
+    parseStmts("f(); return x")
+    parseStmts(
+      """var x = { baz }
+        |return x
+        |""".stripMargin)
+
+    assertEquals(
+      parseStmts(
+        """f()
+          |g()
+          |""".stripMargin),
+      parseStmts(
+        """f();
+          |return g()
+          |""".stripMargin))
+
+      parseStmts(
+        """ val next = f() // Comment
+          | g()
+          |""".stripMargin)
+
+      assertEquals(
+        parseStmts(
+          """f()
+            |
+            |()
+            |""".stripMargin),
+        parseStmts(
+          """f();
+            |
+            |()
+            |""".stripMargin))
+  }
+
+  test("Simple patterns") {
+    parseMatchPattern("x")
+    parseMatchPattern("Cons(x, y)")
+    assertEquals(
+      parseMatchPattern("_"),
+      IgnorePattern())
+    parseMatchPattern("Cons(x, Cons(x, Nil()))")
+
+    assertEquals(
+      parseMatchPattern("(left, Cons(x, right))"),
+        TagPattern(IdRef(List("effekt"), "Tuple2"),
+          List(AnyPattern(IdDef("left")),
+          TagPattern(IdRef(List(), "Cons"), List(AnyPattern(IdDef("x")), AnyPattern(IdDef("right")))))))
+  }
+
+  test("Block arguments") {
+    parseExpr("map {f}")
+    parseExpr("map {list::f}")
+    parseExpr("map {f} {g}")
+    parseExpr("map { f } { g }")
+    parseExpr("map(x) { f } { g }")
+    parseExpr("map(x) { return 42 }")
+    parseExpr("map(x) { map(x) { return 42 } }")
+  }
+
+  test("Value types") {
+    assertEquals(
+      parseValueType("Int"),
+      ValueTypeRef(IdRef(Nil, "Int"), Nil))
+
+    parseValueType("List[Int]")
+    parseValueType("list::List[Int]")
+    parseValueType("list::List[effekt::Int]")
+  }
+
+  test("Block types") {
+    parseBlockType("Exc")
+    parseBlockType("State[S]")
+    parseBlockType("State[Int]")
+    parseBlockType("() => Int")
+    parseBlockType("(Int) => Int")
+    parseBlockType("Int => Int")
+
+    assertEquals(
+      parseBlockType("(Int, String) => Int"),
+      FunctionType(Nil, List(ValueTypeRef(IdRef(Nil,"Int"), Nil),
+        ValueTypeRef(IdRef(Nil,"String"), Nil)), Nil, ValueTypeRef(IdRef(Nil, "Int"), Nil), Effects(Nil)))
+
+    parseBlockType("(Int, String) => Int / Exc")
+    parseBlockType("[T](Int, String) => Int / { Exc, State[T] }")
+    parseBlockType("=> Int")
+    parseBlockType("{Exc} => Int")
+    parseBlockType("{Exc} {Amb} => Int")
+    parseBlockType("({Exc} {Amb} => Int)")
+    parseBlockType("{Exc} {amb : Amb} => Int")
+    parseBlockType("{exc:Exc} => Int")
+    parseBlockType("[T] => T") // Not sure we want this...
+
+    parseValueType("Exc at { a, b, c }")
+    parseValueType("() => (Exc at {}) / {} at { a, b, c }")
+
+    assertEquals(
+      parseValueType("() => Int at { a, b, c }"),
+      parseValueType("(() => Int) at { a, b, c }"))
+
+    // we purposefully cannot parse this:
+    intercept[Throwable] { parseValueType("() => Int at { a, b, c } at {}") }
+
+    parseValueType("() => (Int at { a, b, c }) at {}")
+    parseValueType("(() => Int) at { a, b, c }")
+    parseValueType("(() => Int at {}) => Int at { a, b, c }")
+  }
+
+  test("Params") {
+    parseParams("()")
+    intercept[Throwable] { parseParams("(x, y)") }
+    parseParams("[A, B](x: A, y: B)")
+    intercept[Throwable] { parseParams("[]") }
+    // is this desirable?
+    parseParams("[A]")
+
+
+    parseLambdaParams("a")
+    parseLambdaParams("(a)")
+    parseLambdaParams("(a: Int)")
+    parseLambdaParams("[T](a: Int)")
+    parseLambdaParams("[T](a)")
+    parseLambdaParams("[T]")
+    parseLambdaParams("{f: Exc}")
+    parseLambdaParams("[T]{f: Exc}")
+  }
+
+  test("Match clauses") {
+    parseMatchClause("case x => 42")
+    parseMatchClause("case Foo(x, y) => 42")
+    parseMatchClause("case Foo(x, y) and x == 43 => 42")
+    parseMatchClause("case Foo(Bar(42, true), _) and x == 43 => 42")
+    parseMatchClause("case _ => 42")
+    parseMatchClause("case a and a is Foo(bar) => 42")
+    parseMatchClause("case outer::inner::Constructor(x) => x + 1")
+    parseMatchClause("case () => 2")
+  }
+
+  test("Op clauses") {
+    parseOpClause("def foo() = 42")
+    parseOpClause("def foo[T]() = 42")
+    parseOpClause("def foo[T](a) = 42")
+    parseOpClause("def foo[T](a: Int) = 42")
+    parseOpClause("def foo[T](a: Int, b) = 42")
+    // TODO rebase!
+    // parseOpClause("def foo[T](){f} = 42")
+  }
+
+  test("Implementations") {
+    assertEquals(
+      parseImplementation("Foo {}"),
+      Implementation(BlockTypeRef(IdRef(Nil, "Foo"), Nil), Nil))
+
+    parseImplementation("Foo[T] {}")
+    parseImplementation("Foo[T] { def bar() = 42 }")
+    parseImplementation(
+      """Foo[T] {
+        |  def bar() = 42
+        |  def baz() = 42
+        |}""".stripMargin)
+
+    parseImplementation("Foo[T] { case Get(x) => 43 }")
+
+    // Doesn't work yet
+    parseImplementation("Foo { x => 43 }".stripMargin)
+
+    assertEquals(
+      parseImplementation("Foo { 43 }"),
+      Implementation(
+        BlockTypeRef(IdRef(Nil, "Foo"), Nil),
+        List(OpClause(IdRef(Nil, "Foo"), Nil, Nil, Nil, None,
+          Return(Literal(43, symbols.builtins.TInt)), IdDef("resume")))))
+  }
+
+  test("Try expressions") {
+    parseTry("try 42 with Eff { 42 }")
+    parseTry("try { 42 } with Eff { 42 }")
+    parseTry("try { 42 } with Empty {}")
+    parseTry("try { 42 } with Eff { def op(x) = x + 42 }")
+    parseTry(
+      """try {
+      |  val x = 42
+      |  do op(x + 1)
+      |} with Eff[A] {
+      |  def op(x: A) = x
+      |}
+      """.stripMargin
+    )
+    parseTry(
+      """try { do op(42) }
+      with Eff1 {}
+      with Eff2 { case Get(x) => x + 1 }
+      with Eff3 { def op(x, y) = { x } def op2() = { () }}
+      """
+    )
+  }
+
+  test("Type definition") {
+    parseDefinition("type A = Int")
+    parseDefinition("type A[X] = Int")
+    parseDefinition("type A[X] { Foo() }")
+    parseDefinition(
+      """type A[X] {
+        |  Foo();
+        |  Bar()
+        |}
+        |""".stripMargin)
+
+    parseDefinition(
+      """type DATATYPE[X] {
+        |  Foo()
+        |  Bar()
+        |}
+        |""".stripMargin)
+  }
+
+  test("Namespaces") {
+    parseDefinitions(
+      """val x = 4
+        |val y = 5
+        |""".stripMargin)
+
+    val nested = parseDefinitions(
+      """namespace list {
+        |  val x = 4
+        |  val y = 5
+        |}
+        |""".stripMargin)
+
+    val semi = parseDefinitions(
+      """namespace list;
+        |val x = 4
+        |val y = 5
+        |""".stripMargin)
+
+    assertEquals(nested, semi)
+
+    val nested2 = parseDefinitions(
+      """namespace list {
+        |  namespace internal {
+        |
+        |    val x = 4
+        |    val y = 5
+        |  }
+        |}
+        |""".stripMargin)
+
+    val semi2 = parseDefinitions(
+      """namespace list;
+        |namespace internal;
+        |
+        |val x = 4
+        |val y = 5
+        |""".stripMargin)
+
+    val semiInsertion = parseDefinitions(
+      """namespace list
+        |namespace internal
+        |
+        |val x = 4
+        |val y = 5
+        |""".stripMargin)
+
+    assertEquals(nested2, semi2)
+    assertEquals(nested2, semiInsertion)
+
+    parseDefinitions(
+      """val x = {
+        |  namespace foo;
+        |  val y = 4;
+        |  foo::y
+        |}
+        |""".stripMargin)
+  }
+
+  test("Function definitions") {
+    assertEquals(
+      parseDefinition(
+        """def foo = f
+          |""".stripMargin),
+      DefDef(IdDef("foo"), None, Var(IdRef(Nil, "f"))))
+
+    parseDefinition(
+        """def foo: Exc = f
+          |""".stripMargin)
+
+    parseDefinition(
+        """def foo() = e
+          |""".stripMargin)
+
+    parseDefinition(
+        """def foo[T] = e
+          |""".stripMargin)
+
+    parseDefinition(
+        """def foo[T](x: Int) = e
+          |""".stripMargin)
+
+    parseDefinition(
+        """def foo[T](x: Int): String / {} = e
+          |""".stripMargin)
+  }
+
+  test("Toplevel definitions") {
+    parseToplevel("def foo() = ()")
+    parseToplevel("effect Foo = {}")
+    parseToplevel("effect Foo(): Int")
+    parseToplevel("interface Foo {}")
+
+    parseToplevel(
+      """interface State {
+        |  def get(): Int
+        |  def set(n: Int): Unit
+        |}
+        |""".stripMargin)
+
+    parseToplevel(
+      """interface State[T] {
+        |  def get(): T
+        |  def set(n: T): Unit
+        |}
+        |""".stripMargin)
+
+    parseToplevel(
+      """extern include "foo/text.txt"
+        |""".stripMargin)
+
+    parseToplevel("extern type Foo[S]")
+    parseToplevel("extern resource foo: Foo")
+
+    parseToplevel(
+      """extern "function() { console.log('hey!'); }"
+        |""".stripMargin)
+
+    parseToplevel(
+      """extern def foo(): Int = "bar ${test} baz ${bam}"
+        |""".stripMargin)
+  }
+
+  test("Programs") {
+    // this is from examples/pos/builtins
+    parseProgram(
+      """module examples/pos/builtins
+        |
+        |type Color { Red(); Green(); Blue() }
+        |
+        |def main() = {
+        |    println(1);
+        |    println("foo");
+        |    println(true);
+        |    println(1 == 2);
+        |    inspect(Red())
+        |}
+        |""".stripMargin)
+
+    parseProgram(
+      """// test comment
+        |def foo() = 42
+        |""".stripMargin)
+
+    parseProgram(
+      """module trailing_comment
+        |
+        |def main() = ()
+        |
+        |// foo""".stripMargin)
+  }
+
+  test("Extern definition") {
+    parseExternDef("extern {io} def read(s: String): Int = default { 42 } js { 1 + 1 } chez { 42 }")
+    parseExternDef("extern \"console.log(42)\"")
+    parseExternDef("extern \"\"\"console.log(42)\"\"\"")
+    parseExternDef("extern type Complex")
+    parseExternDef("extern type State[A]")
+    parseExternDef("extern interface State[A]")
+    parseExternDef("extern resource withFile: [A](String) { () => A } => A")
+    parseExternDef("extern include \"path/to/file\"")
+    parseExternDef("extern js \"\"\"console.log(42)\"\"\"")
+    parseExternDef("extern pure def read(s: String): String = default { s }")
+    parseExternDef("extern def read(s: String): String = \"${s}\"")
+    parseProgram(
+      "extern def println(value: String): Unit =" +
+      "js \"$effekt.println(${value})\"" +
+      "chez \"(println_impl ${value})\"" +
+      "ml { print(value); print(\"\\n\") }" +
+      "llvm \"\"\"call void @c_io_println_String(%Pos %value); ret %Pos zeroinitializer ; Unit\"\"\"" + "\n" +
+      "extern js \"\"\" function \"\"\""
+    )
+  }
+}