// Generated automatically from src/module/repl.wren. Do not edit.
static const char* replModuleSource =
"import \"meta\" for Meta\n"
"import \"io\" for Stdin\n"
"import \"os\" for Platform\n"
"\n"
"/// Abstract base class for the REPL. Manages the input line and history, but\n"
"/// does not render.\n"
"class Repl {\n"
"  construct new() {\n"
"    _cursor = 0\n"
"    _line = \"\"\n"
"\n"
"    _history = []\n"
"    _historyIndex = 0\n"
"\n"
"    // Whether or not we allow ASCI escape sequences for showing colors and\n"
"    // moving the cursor. When this is false, the REPL has reduced\n"
"    // functionality.\n"
"    _allowAnsi = false\n"
"  }\n"
"\n"
"  cursor { _cursor }\n"
"  cursor=(value) { _cursor = value }\n"
"  line { _line }\n"
"  line=(value) { _line = value }\n"
"\n"
"  run() {\n"
"    Stdin.isRaw = true\n"
"    refreshLine(false)\n"
"\n"
"    while (true) {\n"
"      var byte = Stdin.readByte()\n"
"      if (handleChar(byte)) break\n"
"      refreshLine(true)\n"
"    }\n"
"  }\n"
"\n"
"  handleChar(byte) {\n"
"    if (byte == Chars.ctrlC) {\n"
"      System.print()\n"
"      return true\n"
"    } else if (byte == Chars.ctrlD) {\n"
"      // If the line is empty, Ctrl_D exits.\n"
"      if (_line.isEmpty) {\n"
"        System.print()\n"
"        return true\n"
"      }\n"
"\n"
"      // Otherwise, it deletes the character after the cursor.\n"
"      deleteRight()\n"
"    } else if (byte == Chars.tab) {\n"
"      var completion = getCompletion()\n"
"      if (completion != null) {\n"
"        _line = _line + completion\n"
"        _cursor = _line.count\n"
"      }\n"
"    } else if (byte == Chars.ctrlU) {\n"
"      // Clear the line.\n"
"      _line = \"\"\n"
"      _cursor = 0\n"
"    } else if (byte == Chars.ctrlN) {\n"
"      nextHistory()\n"
"    } else if (byte == Chars.ctrlP) {\n"
"      previousHistory()\n"
"    } else if (byte == Chars.escape) {\n"
"      var escapeType = Stdin.readByte()\n"
"      var value = Stdin.readByte()\n"
"      if (escapeType == Chars.leftBracket) {\n"
"        // ESC [ sequence.\n"
"        handleEscapeBracket(value)\n"
"      } else {\n"
"        // TODO: Handle ESC 0 sequences.\n"
"      }\n"
"    } else if (byte == Chars.carriageReturn) {\n"
"      executeInput()\n"
"    } else if (byte == Chars.delete) {\n"
"      deleteLeft()\n"
"    } else if (byte >= Chars.space && byte <= Chars.tilde) {\n"
"      insertChar(byte)\n"
"    } else {\n"
"      // TODO: Other shortcuts?\n"
"      System.print(\"Unhandled byte: %(byte)\")\n"
"    }\n"
"\n"
"    return false\n"
"  }\n"
"\n"
"  /// Inserts the character with [byte] value at the current cursor position.\n"
"  insertChar(byte) {\n"
"    var char = String.fromCodePoint(byte)\n"
"    _line = _line[0..._cursor] + char + _line[_cursor..-1]\n"
"    _cursor = _cursor + 1\n"
"  }\n"
"\n"
"  /// Deletes the character before the cursor, if any.\n"
"  deleteLeft() {\n"
"    if (_cursor == 0) return\n"
"\n"
"    // Delete the character before the cursor.\n"
"    _line = _line[0...(_cursor - 1)] + _line[_cursor..-1]\n"
"    _cursor = _cursor - 1\n"
"  }\n"
"\n"
"  /// Deletes the character after the cursor, if any.\n"
"  deleteRight() {\n"
"    if (_cursor == _line.count) return\n"
"\n"
"    // Delete the character after the cursor.\n"
"    _line = _line[0..._cursor] + _line[(_cursor + 1)..-1]\n"
"  }\n"
"\n"
"  handleEscapeBracket(byte) {\n"
"    if (byte == EscapeBracket.up) {\n"
"      previousHistory()\n"
"    } else if (byte == EscapeBracket.down) {\n"
"      nextHistory()\n"
"    }\n"
"  }\n"
"\n"
"  previousHistory() {\n"
"    if (_historyIndex == 0) return\n"
"\n"
"    _historyIndex = _historyIndex - 1\n"
"    _line = _history[_historyIndex]\n"
"    _cursor = _line.count\n"
"  }\n"
"\n"
"  nextHistory() {\n"
"    if (_historyIndex >= _history.count) return\n"
"\n"
"    _historyIndex = _historyIndex + 1\n"
"    if (_historyIndex < _history.count) {\n"
"      _line = _history[_historyIndex]\n"
"      _cursor = _line.count\n"
"    } else {\n"
"      _line = \"\"\n"
"      _cursor = 0\n"
"    }\n"
"  }\n"
"\n"
"  executeInput() {\n"
"    // Remove the completion hint.\n"
"    refreshLine(false)\n"
"\n"
"    // Add it to the history (if the line is interesting).\n"
"    if (_line != \"\" && (_history.isEmpty || _history[-1] != _line)) {\n"
"      _history.add(_line)\n"
"      _historyIndex = _history.count\n"
"    }\n"
"\n"
"    // Reset the current line.\n"
"    var input = _line\n"
"    _line = \"\"\n"
"    _cursor = 0\n"
"\n"
"    System.print()\n"
"\n"
"    // Guess if it looks like a statement or expression. Statements need to be\n"
"    // evaluated at the top level in case they declare variables, but they\n"
"    // don't return a value. Expressions need to have their result displayed.\n"
"    var tokens = lex(input, false)\n"
"\n"
"    // No code, so do nothing.\n"
"    if (tokens.isEmpty) return\n"
"\n"
"    var first = tokens[0]\n"
"    var isStatement =\n"
"        first.type == Token.breakKeyword ||\n"
"        first.type == Token.classKeyword ||\n"
"        first.type == Token.forKeyword ||\n"
"        first.type == Token.foreignKeyword ||\n"
"        first.type == Token.ifKeyword ||\n"
"        first.type == Token.importKeyword ||\n"
"        first.type == Token.returnKeyword ||\n"
"        first.type == Token.varKeyword ||\n"
"        first.type == Token.whileKeyword\n"
"\n"
"    var fiber\n"
"    if (isStatement) {\n"
"      fiber = Fiber.new {\n"
"        // TODO: Should evaluate in main module, not repl's own.\n"
"        Meta.eval(input)\n"
"      }\n"
"\n"
"      var result = fiber.try()\n"
"      if (fiber.error == null) return\n"
"    } else {\n"
"      // TODO: Should evaluate in main module, not repl's own.\n"
"      var function = Meta.compileExpression(input)\n"
"      if (function == null) return\n"
"\n"
"      fiber = Fiber.new(function)\n"
"      var result = fiber.try()\n"
"      if (fiber.error == null) {\n"
"        // TODO: Handle error in result.toString.\n"
"        showResult(result)\n"
"        return\n"
"      }\n"
"    }\n"
"\n"
"    // TODO: Include callstack.\n"
"    showRuntimeError(\"Runtime error: %(fiber.error)\")\n"
"  }\n"
"\n"
"  lex(line, includeWhitespace) {\n"
"    var lexer = Lexer.new(line)\n"
"    var tokens = []\n"
"    while (true) {\n"
"      var token = lexer.readToken()\n"
"      if (token.type == Token.eof) break\n"
"\n"
"      if (includeWhitespace ||\n"
"          (token.type != Token.comment && token.type != Token.whitespace)) {\n"
"        tokens.add(token)\n"
"      }\n"
"    }\n"
"\n"
"    return tokens\n"
"  }\n"
"\n"
"  /// Gets the best possible auto-completion for the current line, or null if\n"
"  /// there is none. The completion is the remaining string to append to the\n"
"  /// line, not the entire completed line.\n"
"  getCompletion() {\n"
"    if (_line.isEmpty) return null\n"
"\n"
"    // Only complete if the cursor is at the end.\n"
"    if (_cursor != _line.count) return null\n"
"\n"
"    for (name in Meta.getModuleVariables(\"repl\")) {\n"
"      // TODO: Also allow completion if the line ends with an identifier but\n"
"      // has other stuff before it.\n"
"      if (name.startsWith(_line)) {\n"
"        return name[_line.count..-1]\n"
"      }\n"
"    }\n"
"  }\n"
"}\n"
"\n"
"/// A reduced functionality REPL that doesn't use ANSI escape sequences.\n"
"class SimpleRepl is Repl {\n"
"  construct new() {\n"
"    super()\n"
"    _erase = \"\"\n"
"  }\n"
"\n"
"  refreshLine(showCompletion) {\n"
"    // A carriage return just moves the cursor to the beginning of the line.\n"
"    // We have to erase it manually. Since we can't use ANSI escapes, and we\n"
"    // don't know how wide the terminal is, erase the longest line we've seen\n"
"    // so far.\n"
"    if (line.count > _erase.count) _erase = \" \" * line.count\n"
"    System.write(\"\r  %(_erase)\")\n"
"\n"
"    // Show the prompt at the beginning of the line.\n"
"    System.write(\"\r> \")\n"
"\n"
"    // Write the line.\n"
"    System.write(line)\n"
"  }\n"
"\n"
"  showResult(value) {\n"
"    // TODO: Syntax color based on type? It might be nice to distinguish\n"
"    // between string results versus stringified results. Otherwise, the\n"
"    // user can't tell the difference between `true` and \"true\".\n"
"    System.print(value)\n"
"  }\n"
"\n"
"  showRuntimeError(message) {\n"
"    System.print(message)\n"
"  }\n"
"}\n"
"\n"
"class AnsiRepl is Repl {\n"
"  construct new() {\n"
"    super()\n"
"  }\n"
"\n"
"  handleChar(byte) {\n"
"    if (byte == Chars.ctrlA) {\n"
"      cursor = 0\n"
"    } else if (byte == Chars.ctrlB) {\n"
"      cursorLeft()\n"
"    } else if (byte == Chars.ctrlE) {\n"
"      cursor = line.count\n"
"    } else if (byte == Chars.ctrlF) {\n"
"      cursorRight()\n"
"    } else if (byte == Chars.ctrlK) {\n"
"      // Delete everything after the cursor.\n"
"      line = line[0...cursor]\n"
"    } else if (byte == Chars.ctrlL) {\n"
"      // Clear the screen.\n"
"      System.write(\"\x1b[2J\")\n"
"      // Move cursor to top left.\n"
"      System.write(\"\x1b[H\")\n"
"    } else {\n"
"      // TODO: Ctrl-T to swap chars.\n"
"      // TODO: ESC H and F to move to beginning and end of line. (Both ESC\n"
"      // [ and ESC 0 sequences?)\n"
"      // TODO: Ctrl-W delete previous word.\n"
"      return super.handleChar(byte)\n"
"    }\n"
"\n"
"    return false\n"
"  }\n"
"\n"
"  handleEscapeBracket(byte) {\n"
"    if (byte == EscapeBracket.left) {\n"
"      cursorLeft()\n"
"    } else if (byte == EscapeBracket.right) {\n"
"      cursorRight()\n"
"    }\n"
"\n"
"    super.handleEscapeBracket(byte)\n"
"  }\n"
"\n"
"  /// Move the cursor left one character.\n"
"  cursorLeft() {\n"
"    if (cursor > 0) cursor = cursor - 1\n"
"  }\n"
"\n"
"  /// Move the cursor right one character.\n"
"  cursorRight() {\n"
"    // TODO: Take into account multi-byte characters?\n"
"    if (cursor < line.count) cursor = cursor + 1\n"
"  }\n"
"\n"
"  refreshLine(showCompletion) {\n"
"    // Erase the whole line.\n"
"    System.write(\"\x1b[2K\")\n"
"\n"
"    // Show the prompt at the beginning of the line.\n"
"    System.write(Color.gray)\n"
"    System.write(\"\r> \")\n"
"    System.write(Color.none)\n"
"\n"
"    // Syntax highlight the line.\n"
"    for (token in lex(line, true)) {\n"
"      if (token.type == Token.eof) break\n"
"\n"
"      System.write(TOKEN_COLORS[token.type])\n"
"      System.write(token.text)\n"
"      System.write(Color.none)\n"
"    }\n"
"\n"
"    if (showCompletion) {\n"
"      var completion = getCompletion()\n"
"      if (completion != null) {\n"
"        System.write(\"%(Color.gray)%(completion)%(Color.none)\")\n"
"      }\n"
"    }\n"
"\n"
"    // Position the cursor.\n"
"    System.write(\"\r\x1b[%(2 + cursor)C\")\n"
"  }\n"
"\n"
"  showResult(value) {\n"
"    // TODO: Syntax color based on type? It might be nice to distinguish\n"
"    // between string results versus stringified results. Otherwise, the\n"
"    // user can't tell the difference between `true` and \"true\".\n"
"    System.print(\"%(Color.brightWhite)%(value)%(Color.none)\")\n"
"  }\n"
"\n"
"  showRuntimeError(message) {\n"
"    System.print(\"%(Color.red)%(message)%(Color.none)\")\n"
"    // TODO: Print entire stack.\n"
"  }\n"
"}\n"
"\n"
"/// ANSI color escape sequences.\n"
"class Color {\n"
"  static none { \"\x1b[0m\" }\n"
"  static black { \"\x1b[30m\" }\n"
"  static red { \"\x1b[31m\" }\n"
"  static green { \"\x1b[32m\" }\n"
"  static yellow { \"\x1b[33m\" }\n"
"  static blue { \"\x1b[34m\" }\n"
"  static magenta { \"\x1b[35m\" }\n"
"  static cyan { \"\x1b[36m\" }\n"
"  static white { \"\x1b[37m\" }\n"
"\n"
"  static gray { \"\x1b[30;1m\" }\n"
"  static pink { \"\x1b[31;1m\" }\n"
"  static brightWhite { \"\x1b[37;1m\" }\n"
"}\n"
"\n"
"/// Utilities for working with characters.\n"
"class Chars {\n"
"  static ctrlA { 0x01 }\n"
"  static ctrlB { 0x02 }\n"
"  static ctrlC { 0x03 }\n"
"  static ctrlD { 0x04 }\n"
"  static ctrlE { 0x05 }\n"
"  static ctrlF { 0x06 }\n"
"  static tab { 0x09 }\n"
"  static lineFeed { 0x0a }\n"
"  static ctrlK { 0x0b }\n"
"  static ctrlL { 0x0c }\n"
"  static carriageReturn { 0x0d }\n"
"  static ctrlN { 0x0e }\n"
"  static ctrlP { 0x10 }\n"
"  static ctrlU { 0x15 }\n"
"  static escape { 0x1b }\n"
"  static space { 0x20 }\n"
"  static bang { 0x21 }\n"
"  static quote { 0x22 }\n"
"  static percent { 0x25 }\n"
"  static amp { 0x26 }\n"
"  static leftParen { 0x28 }\n"
"  static rightParen { 0x29 }\n"
"  static star { 0x2a }\n"
"  static plus { 0x2b }\n"
"  static comma { 0x2c }\n"
"  static minus { 0x2d }\n"
"  static dot { 0x2e }\n"
"  static slash { 0x2f }\n"
"\n"
"  static zero { 0x30 }\n"
"  static nine { 0x39 }\n"
"\n"
"  static colon { 0x3a }\n"
"  static less { 0x3c }\n"
"  static equal { 0x3d }\n"
"  static greater { 0x3e }\n"
"  static question { 0x3f }\n"
"\n"
"  static upperA { 0x41 }\n"
"  static upperF { 0x46 }\n"
"  static upperZ { 0x5a }\n"
"\n"
"  static leftBracket { 0x5b }\n"
"  static backslash { 0x5c }\n"
"  static rightBracket { 0x5d }\n"
"  static caret { 0x5e }\n"
"  static underscore { 0x5f }\n"
"\n"
"  static lowerA { 0x61 }\n"
"  static lowerF { 0x66 }\n"
"  static lowerX { 0x78 }\n"
"  static lowerZ { 0x7a }\n"
"\n"
"  static leftBrace { 0x7b }\n"
"  static pipe { 0x7c }\n"
"  static rightBrace { 0x7d }\n"
"  static tilde { 0x7e }\n"
"  static delete { 0x7f }\n"
"\n"
"  static isAlpha(c) {\n"
"    return c >= lowerA && c <= lowerZ ||\n"
"           c >= upperA && c <= upperZ ||\n"
"           c == underscore\n"
"  }\n"
"\n"
"  static isDigit(c) { c >= zero && c <= nine }\n"
"\n"
"  static isAlphaNumeric(c) { isAlpha(c) || isDigit(c) }\n"
"\n"
"  static isHexDigit(c) {\n"
"    return c >= zero && c <= nine ||\n"
"           c >= lowerA && c <= lowerF ||\n"
"           c >= upperA && c <= upperF\n"
"  }\n"
"\n"
"  static isLowerAlpha(c) { c >= lowerA && c <= lowerZ }\n"
"\n"
"  static isWhitespace(c) { c == space || c == tab || c == carriageReturn }\n"
"}\n"
"\n"
"class EscapeBracket {\n"
"  static up { 0x41 }\n"
"  static down { 0x42 }\n"
"  static right { 0x43 }\n"
"  static left { 0x44 }\n"
"}\n"
"\n"
"class Token {\n"
"  // Punctuators.\n"
"  static leftParen { \"leftParen\" }\n"
"  static rightParen { \"rightParen\" }\n"
"  static leftBracket { \"leftBracket\" }\n"
"  static rightBracket { \"rightBracket\" }\n"
"  static leftBrace { \"leftBrace\" }\n"
"  static rightBrace { \"rightBrace\" }\n"
"  static colon { \"colon\" }\n"
"  static dot { \"dot\" }\n"
"  static dotDot { \"dotDot\" }\n"
"  static dotDotDot { \"dotDotDot\" }\n"
"  static comma { \"comma\" }\n"
"  static star { \"star\" }\n"
"  static slash { \"slash\" }\n"
"  static percent { \"percent\" }\n"
"  static plus { \"plus\" }\n"
"  static minus { \"minus\" }\n"
"  static pipe { \"pipe\" }\n"
"  static pipePipe { \"pipePipe\" }\n"
"  static caret { \"caret\" }\n"
"  static amp { \"amp\" }\n"
"  static ampAmp { \"ampAmp\" }\n"
"  static question { \"question\" }\n"
"  static bang { \"bang\" }\n"
"  static tilde { \"tilde\" }\n"
"  static equal { \"equal\" }\n"
"  static less { \"less\" }\n"
"  static lessEqual { \"lessEqual\" }\n"
"  static lessLess { \"lessLess\" }\n"
"  static greater { \"greater\" }\n"
"  static greaterEqual { \"greaterEqual\" }\n"
"  static greaterGreater { \"greaterGreater\" }\n"
"  static equalEqual { \"equalEqual\" }\n"
"  static bangEqual { \"bangEqual\" }\n"
"\n"
"  // Keywords.\n"
"  static breakKeyword { \"break\" }\n"
"  static classKeyword { \"class\" }\n"
"  static constructKeyword { \"construct\" }\n"
"  static elseKeyword { \"else\" }\n"
"  static falseKeyword { \"false\" }\n"
"  static forKeyword { \"for\" }\n"
"  static foreignKeyword { \"foreign\" }\n"
"  static ifKeyword { \"if\" }\n"
"  static importKeyword { \"import\" }\n"
"  static inKeyword { \"in\" }\n"
"  static isKeyword { \"is\" }\n"
"  static nullKeyword { \"null\" }\n"
"  static returnKeyword { \"return\" }\n"
"  static staticKeyword { \"static\" }\n"
"  static superKeyword { \"super\" }\n"
"  static thisKeyword { \"this\" }\n"
"  static trueKeyword { \"true\" }\n"
"  static varKeyword { \"var\" }\n"
"  static whileKeyword { \"while\" }\n"
"\n"
"  static field { \"field\" }\n"
"  static name { \"name\" }\n"
"  static number { \"number\" }\n"
"  static string { \"string\" }\n"
"  static interpolation { \"interpolation\" }\n"
"  static comment { \"comment\" }\n"
"  static whitespace { \"whitespace\" }\n"
"  static line { \"line\" }\n"
"  static error { \"error\" }\n"
"  static eof { \"eof\" }\n"
"\n"
"  construct new(source, type, start, length) {\n"
"    _source = source\n"
"    _type = type\n"
"    _start = start\n"
"    _length = length\n"
"  }\n"
"\n"
"  type { _type }\n"
"  text { _source[_start...(_start + _length)] }\n"
"\n"
"  start { _start }\n"
"  length { _length }\n"
"\n"
"  toString { text }\n"
"}\n"
"\n"
"var KEYWORDS = {\n"
"  \"break\": Token.breakKeyword,\n"
"  \"class\": Token.classKeyword,\n"
"  \"construct\": Token.constructKeyword,\n"
"  \"else\": Token.elseKeyword,\n"
"  \"false\": Token.falseKeyword,\n"
"  \"for\": Token.forKeyword,\n"
"  \"foreign\": Token.foreignKeyword,\n"
"  \"if\": Token.ifKeyword,\n"
"  \"import\": Token.importKeyword,\n"
"  \"in\": Token.inKeyword,\n"
"  \"is\": Token.isKeyword,\n"
"  \"null\": Token.nullKeyword,\n"
"  \"return\": Token.returnKeyword,\n"
"  \"static\": Token.staticKeyword,\n"
"  \"super\": Token.superKeyword,\n"
"  \"this\": Token.thisKeyword,\n"
"  \"true\": Token.trueKeyword,\n"
"  \"var\": Token.varKeyword,\n"
"  \"while\": Token.whileKeyword\n"
"}\n"
"\n"
"var TOKEN_COLORS = {\n"
"  Token.leftParen: Color.gray,\n"
"  Token.rightParen: Color.gray,\n"
"  Token.leftBracket: Color.gray,\n"
"  Token.rightBracket: Color.gray,\n"
"  Token.leftBrace: Color.gray,\n"
"  Token.rightBrace: Color.gray,\n"
"  Token.colon: Color.gray,\n"
"  Token.dot: Color.gray,\n"
"  Token.dotDot: Color.none,\n"
"  Token.dotDotDot: Color.none,\n"
"  Token.comma: Color.gray,\n"
"  Token.star: Color.none,\n"
"  Token.slash: Color.none,\n"
"  Token.percent: Color.none,\n"
"  Token.plus: Color.none,\n"
"  Token.minus: Color.none,\n"
"  Token.pipe: Color.none,\n"
"  Token.pipePipe: Color.none,\n"
"  Token.caret: Color.none,\n"
"  Token.amp: Color.none,\n"
"  Token.ampAmp: Color.none,\n"
"  Token.question: Color.none,\n"
"  Token.bang: Color.none,\n"
"  Token.tilde: Color.none,\n"
"  Token.equal: Color.none,\n"
"  Token.less: Color.none,\n"
"  Token.lessEqual: Color.none,\n"
"  Token.lessLess: Color.none,\n"
"  Token.greater: Color.none,\n"
"  Token.greaterEqual: Color.none,\n"
"  Token.greaterGreater: Color.none,\n"
"  Token.equalEqual: Color.none,\n"
"  Token.bangEqual: Color.none,\n"
"\n"
"  // Keywords.\n"
"  Token.breakKeyword: Color.cyan,\n"
"  Token.classKeyword: Color.cyan,\n"
"  Token.constructKeyword: Color.cyan,\n"
"  Token.elseKeyword: Color.cyan,\n"
"  Token.falseKeyword: Color.cyan,\n"
"  Token.forKeyword: Color.cyan,\n"
"  Token.foreignKeyword: Color.cyan,\n"
"  Token.ifKeyword: Color.cyan,\n"
"  Token.importKeyword: Color.cyan,\n"
"  Token.inKeyword: Color.cyan,\n"
"  Token.isKeyword: Color.cyan,\n"
"  Token.nullKeyword: Color.cyan,\n"
"  Token.returnKeyword: Color.cyan,\n"
"  Token.staticKeyword: Color.cyan,\n"
"  Token.superKeyword: Color.cyan,\n"
"  Token.thisKeyword: Color.cyan,\n"
"  Token.trueKeyword: Color.cyan,\n"
"  Token.varKeyword: Color.cyan,\n"
"  Token.whileKeyword: Color.cyan,\n"
"\n"
"  Token.field: Color.none,\n"
"  Token.name: Color.none,\n"
"  Token.number: Color.magenta,\n"
"  Token.string: Color.yellow,\n"
"  Token.interpolation: Color.yellow,\n"
"  Token.comment: Color.gray,\n"
"  Token.whitespace: Color.none,\n"
"  Token.line: Color.none,\n"
"  Token.error: Color.red,\n"
"  Token.eof: Color.none,\n"
"}\n"
"\n"
"// Data table for tokens that are tokenized using maximal munch.\n"
"//\n"
"// The key is the character that starts the token or tokens. After that is a\n"
"// list of token types and characters. As long as the next character is matched,\n"
"// the type will update to the type after that character.\n"
"var PUNCTUATORS = {\n"
"  Chars.leftParen: [Token.leftParen],\n"
"  Chars.rightParen: [Token.rightParen],\n"
"  Chars.leftBracket: [Token.leftBracket],\n"
"  Chars.rightBracket: [Token.rightBracket],\n"
"  Chars.leftBrace: [Token.leftBrace],\n"
"  Chars.rightBrace: [Token.rightBrace],\n"
"  Chars.colon: [Token.colon],\n"
"  Chars.comma: [Token.comma],\n"
"  Chars.star: [Token.star],\n"
"  Chars.percent: [Token.percent],\n"
"  Chars.plus: [Token.plus],\n"
"  Chars.minus: [Token.minus],\n"
"  Chars.tilde: [Token.tilde],\n"
"  Chars.caret: [Token.caret],\n"
"  Chars.question: [Token.question],\n"
"  Chars.lineFeed: [Token.line],\n"
"\n"
"  Chars.pipe: [Token.pipe, Chars.pipe, Token.pipePipe],\n"
"  Chars.amp: [Token.amp, Chars.amp, Token.ampAmp],\n"
"  Chars.bang: [Token.bang, Chars.equal, Token.bangEqual],\n"
"  Chars.equal: [Token.equal, Chars.equal, Token.equalEqual],\n"
"\n"
"  Chars.dot: [Token.dot, Chars.dot, Token.dotDot, Chars.dot, Token.dotDotDot]\n"
"}\n"
"\n"
"/// Tokenizes a string of input. This lexer differs from most in that it\n"
"/// silently ignores errors from incomplete input, like a string literal with\n"
"/// no closing quote. That's because this is intended to be run on a line of\n"
"/// input while the user is still typing it.\n"
"class Lexer {\n"
"  construct new(source) {\n"
"    _source = source\n"
"\n"
"    // Due to the magic of UTF-8, we can safely treat Wren source as a series\n"
"    // of bytes, since the only code points that are meaningful to Wren fit in\n"
"    // ASCII. The only place where non-ASCII code points can occur is inside\n"
"    // string literals and comments and the lexer safely treats those as opaque\n"
"    // bytes.\n"
"    _bytes = source.bytes\n"
"\n"
"    _start = 0\n"
"    _current = 0\n"
"\n"
"    // The stack of ongoing interpolated strings. Each element in the list is\n"
"    // a single level of interpolation nesting. The value of the element is the\n"
"    // number of unbalanced \"(\" still remaining to be closed.\n"
"    _interpolations = []\n"
"  }\n"
"\n"
"  readToken() {\n"
"    if (_current >= _bytes.count) return makeToken(Token.eof)\n"
"\n"
"    _start = _current\n"
"    var c = _bytes[_current]\n"
"    advance()\n"
"\n"
"    if (!_interpolations.isEmpty) {\n"
"      if (c == Chars.leftParen) {\n"
"        _interpolations[-1] = _interpolations[-1] + 1\n"
"      } else if (c == Chars.rightParen) {\n"
"        _interpolations[-1] = _interpolations[-1] - 1\n"
"\n"
"        // The last \")\" in an interpolated expression ends the expression and\n"
"        // resumes the string.\n"
"        if (_interpolations[-1] == 0) {\n"
"          // This is the final \")\", so the interpolation expression has ended.\n"
"          // This \")\" now begins the next section of the template string.\n"
"          _interpolations.removeAt(-1)\n"
"          return readString()\n"
"        }\n"
"      }\n"
"    }\n"
"\n"
"    if (PUNCTUATORS.containsKey(c)) {\n"
"      var punctuator = PUNCTUATORS[c]\n"
"      var type = punctuator[0]\n"
"      var i = 1\n"
"      while (i < punctuator.count) {\n"
"        if (!match(punctuator[i])) break\n"
"        type = punctuator[i + 1]\n"
"        i = i + 2\n"
"      }\n"
"\n"
"      return makeToken(type)\n"
"    }\n"
"\n"
"    // Handle \"<\", \"<<\", and \"<=\".\n"
"    if (c == Chars.less) {\n"
"      if (match(Chars.less)) return makeToken(Token.lessLess)\n"
"      if (match(Chars.equal)) return makeToken(Token.lessEqual)\n"
"      return makeToken(Token.less)\n"
"    }\n"
"\n"
"    // Handle \">\", \">>\", and \">=\".\n"
"    if (c == Chars.greater) {\n"
"      if (match(Chars.greater)) return makeToken(Token.greaterGreater)\n"
"      if (match(Chars.equal)) return makeToken(Token.greaterEqual)\n"
"      return makeToken(Token.greater)\n"
"    }\n"
"\n"
"    // Handle \"/\", \"//\", and \"/*\".\n"
"    if (c == Chars.slash) {\n"
"      if (match(Chars.slash)) return readLineComment()\n"
"      if (match(Chars.star)) return readBlockComment()\n"
"      return makeToken(Token.slash)\n"
"    }\n"
"\n"
"    if (c == Chars.underscore) return readField()\n"
"    if (c == Chars.quote) return readString()\n"
"\n"
"    if (c == Chars.zero && peek() == Chars.lowerX) return readHexNumber()\n"
"    if (Chars.isWhitespace(c)) return readWhitespace()\n"
"    if (Chars.isDigit(c)) return readNumber()\n"
"    if (Chars.isAlpha(c)) return readName()\n"
"\n"
"    return makeToken(Token.error)\n"
"  }\n"
"\n"
"  // Reads a line comment until the end of the line is reached.\n"
"  readLineComment() {\n"
"    // A line comment stops at the newline since newlines are significant.\n"
"    while (peek() != Chars.lineFeed && !isAtEnd) {\n"
"      advance()\n"
"    }\n"
"\n"
"    return makeToken(Token.comment)\n"
"  }\n"
"\n"
"  readBlockComment() {\n"
"    // Block comments can nest.\n"
"    var nesting = 1\n"
"    while (nesting > 0) {\n"
"      // TODO: Report error.\n"
"      if (isAtEnd) break\n"
"\n"
"      if (peek() == Chars.slash && peek(1) == Chars.star) {\n"
"        advance()\n"
"        advance()\n"
"        nesting = nesting + 1\n"
"      } else if (peek() == Chars.star && peek(1) == Chars.slash) {\n"
"        advance()\n"
"        advance()\n"
"        nesting = nesting - 1\n"
"        if (nesting == 0) break\n"
"      } else {\n"
"        advance()\n"
"      }\n"
"    }\n"
"\n"
"    return makeToken(Token.comment)\n"
"  }\n"
"\n"
"  // Reads a static or instance field.\n"
"  readField() {\n"
"    var type = Token.field\n"
"\n"
"    // Read the rest of the name.\n"
"    while (match {|c| Chars.isAlphaNumeric(c) }) {}\n"
"\n"
"    return makeToken(type)\n"
"  }\n"
"\n"
"  // Reads a string literal.\n"
"  readString() {\n"
"    var type = Token.string\n"
"\n"
"    while (!isAtEnd) {\n"
"      var c = _bytes[_current]\n"
"      advance()\n"
"\n"
"      if (c == Chars.backslash) {\n"
"        // TODO: Process specific escapes and validate them.\n"
"        advance()\n"
"      } else if (c == Chars.percent) {\n"
"        // Consume the '('.\n"
"        if (!isAtEnd) advance()\n"
"        // TODO: Handle missing '('.\n"
"        _interpolations.add(1)\n"
"        type = Token.interpolation\n"
"        break\n"
"      } else if (c == Chars.quote) {\n"
"        break\n"
"      }\n"
"    }\n"
"\n"
"    return makeToken(type)\n"
"  }\n"
"\n"
"  // Reads a number literal.\n"
"  readHexNumber() {\n"
"    // Skip past the `x`.\n"
"    advance()\n"
"\n"
"    // Read the rest of the number.\n"
"    while (match {|c| Chars.isHexDigit(c) }) {}\n"
"    return makeToken(Token.number)\n"
"  }\n"
"\n"
"  // Reads a series of whitespace characters.\n"
"  readWhitespace() {\n"
"    // Read the rest of the whitespace.\n"
"    while (match {|c| Chars.isWhitespace(c) }) {}\n"
"\n"
"    return makeToken(Token.whitespace)\n"
"  }\n"
"\n"
"  // Reads a number literal.\n"
"  readNumber() {\n"
"    // Read the rest of the number.\n"
"    while (match {|c| Chars.isDigit(c) }) {}\n"
"\n"
"    // TODO: Floating point, scientific.\n"
"    return makeToken(Token.number)\n"
"  }\n"
"\n"
"  // Reads an identifier or keyword token.\n"
"  readName() {\n"
"    // Read the rest of the name.\n"
"    while (match {|c| Chars.isAlphaNumeric(c) }) {}\n"
"\n"
"    var text = _source[_start..._current]\n"
"    var type = Token.name\n"
"    if (KEYWORDS.containsKey(text)) {\n"
"      type = KEYWORDS[text]\n"
"    }\n"
"\n"
"    return Token.new(_source, type, _start, _current - _start)\n"
"  }\n"
"\n"
"  // Returns `true` if we have scanned all characters.\n"
"  isAtEnd { _current >= _bytes.count }\n"
"\n"
"  // Advances past the current character.\n"
"  advance() {\n"
"    _current = _current + 1\n"
"  }\n"
"\n"
"  // Returns the byte value of the current character.\n"
"  peek() { peek(0) }\n"
"\n"
"  // Returns the byte value of the character [n] bytes past the current\n"
"  // character.\n"
"  peek(n) {\n"
"    if (_current + n >= _bytes.count) return -1\n"
"    return _bytes[_current + n]\n"
"  }\n"
"\n"
"  // Consumes the current character if it matches [condition], which can be a\n"
"  // numeric code point value or a function that takes a code point and returns\n"
"  // `true` if the code point matches.\n"
"  match(condition) {\n"
"    if (isAtEnd) return false\n"
"\n"
"    var c = _bytes[_current]\n"
"    if (condition is Fn) {\n"
"      if (!condition.call(c)) return false\n"
"    } else if (c != condition) {\n"
"      return false\n"
"    }\n"
"\n"
"    advance()\n"
"    return true\n"
"  }\n"
"\n"
"  // Creates a token of [type] from the current character range.\n"
"  makeToken(type) { Token.new(_source, type, _start, _current - _start) }\n"
"}\n"
"\n"
"// Fire up the REPL. We use ANSI when talking to a POSIX TTY.\n"
"if (Platform.isPosix && Stdin.isTerminal) {\n"
"  AnsiRepl.new().run()\n"
"} else {\n"
"  // ANSI escape sequences probably aren't supported, so degrade.\n"
"  SimpleRepl.new().run()\n"
"}\n";
