/* Generated by re2swift */
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 1)
// re2swift $INPUT -o $OUTPUT --api generic -fc
import Foundation

func log(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
  var stderr = FileHandle.standardError
  print(items, separator: separator, terminator: terminator, to: &stderr)
#endif
}

extension FileHandle: @retroactive TextOutputStream {
  public func write(_ string: String) {
    self.write(Data(string.utf8))
  }
}

struct State {
  // Use a small buffer to cover the case when a lexeme doesn't fit.
  // In a real world scenario, use a larger buffer.
  static let bufferSize = 10

  var limit  = Self.bufferSize
  var cursor = Self.bufferSize
  var marker = Self.bufferSize
  var token  = Self.bufferSize
  var state  = -1

  let file: FileHandle
  // Buffer is zero-initialized, no need to write sentinel
  var buffer = ContiguousArray<UInt8>(repeating: 0, count: Self.bufferSize + 1)
}

extension State {
  mutating func lex(numbers: inout Int, words: inout Int) -> Status {
    
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 39)
let yycINIT: Int = 0
let yycSPACES: Int = 6
let yycNUMBER: Int = 11
let yycWORD: Int = 16
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 35)

    var yych: UInt8 = 0
    lex: while true {
      self.token = self.cursor
      
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 50)
  var yystate: Int = self.state
  yyl: while true {
    switch yystate {
      case -1...0:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x09:
            fallthrough
          case 0x20:
            self.cursor += 1
            yystate = 2
            continue yyl
          case 0x30...0x39:
            self.cursor += 1
            yystate = 3
            continue yyl
          case 0x61...0x7A:
            self.cursor += 1
            yystate = 4
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 21
              return .waiting
            }
            self.cursor += 1
            yystate = 1
            continue yyl
        }
      case 1:
        self.state = yycINIT
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 56)
        return .badPacket
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 84)
      case 2:
        self.cursor += -1
        self.state = yycSPACES
        yystate = yycSPACES
        continue yyl
      case 3:
        self.cursor += -1
        self.state = yycNUMBER
        yystate = yycNUMBER
        continue yyl
      case 4:
        self.cursor += -1
        self.state = yycWORD
        yystate = yycWORD
        continue yyl
      case 5:
        self.state = yycINIT
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 57)
        return .end
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 104)
      case 6:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x09:
            fallthrough
          case 0x20:
            self.cursor += 1
            yystate = 8
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 22
              return .waiting
            }
            self.cursor += 1
            yystate = 7
            continue yyl
        }
      case 7:
        self.state = yycSPACES
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 56)
        return .badPacket
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 127)
      case 8:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x09:
            fallthrough
          case 0x20:
            self.cursor += 1
            yystate = 8
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 23
              return .waiting
            }
            yystate = 9
            continue yyl
        }
      case 9:
        self.state = yycINIT
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 63)
        continue lex
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 149)
      case 10:
        self.state = yycSPACES
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 57)
        return .end
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 154)
      case 11:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x30...0x39:
            self.cursor += 1
            yystate = 13
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 24
              return .waiting
            }
            self.cursor += 1
            yystate = 12
            continue yyl
        }
      case 12:
        self.state = yycNUMBER
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 56)
        return .badPacket
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 175)
      case 13:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x30...0x39:
            self.cursor += 1
            yystate = 13
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 25
              return .waiting
            }
            yystate = 14
            continue yyl
        }
      case 14:
        self.state = yycSPACES
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 65)
        
          numbers += 1
          continue lex

#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 198)
      case 15:
        self.state = yycNUMBER
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 57)
        return .end
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 203)
      case 16:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x61...0x7A:
            self.cursor += 1
            yystate = 18
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 26
              return .waiting
            }
            self.cursor += 1
            yystate = 17
            continue yyl
        }
      case 17:
        self.state = yycWORD
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 56)
        return .badPacket
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 224)
      case 18:
        yych = self.buffer[self.cursor]
        switch yych {
          case 0x61...0x7A:
            self.cursor += 1
            yystate = 18
            continue yyl
          default:
            if self.limit <= self.cursor {
              self.state = 27
              return .waiting
            }
            yystate = 19
            continue yyl
        }
      case 19:
        self.state = yycSPACES
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 69)
        
          words += 1
          continue lex

#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 247)
      case 20:
        self.state = yycWORD
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 57)
        return .end
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.swift", line: 252)
      case 21:
        if self.limit <= self.cursor {
          yystate = 5
          continue yyl
        }
        yystate = 0
        continue yyl
      case 22:
        if self.limit <= self.cursor {
          yystate = 10
          continue yyl
        }
        yystate = 6
        continue yyl
      case 23:
        if self.limit <= self.cursor {
          yystate = 9
          continue yyl
        }
        yystate = 8
        continue yyl
      case 24:
        if self.limit <= self.cursor {
          yystate = 15
          continue yyl
        }
        yystate = 11
        continue yyl
      case 25:
        if self.limit <= self.cursor {
          yystate = 14
          continue yyl
        }
        yystate = 13
        continue yyl
      case 26:
        if self.limit <= self.cursor {
          yystate = 20
          continue yyl
        }
        yystate = 16
        continue yyl
      case 27:
        if self.limit <= self.cursor {
          yystate = 19
          continue yyl
        }
        yystate = 18
        continue yyl
      default: fatalError("internal lexer error")
    }
  }
#sourceLocation(file: "codegen/swift/002_storable_state_with_conditions.re", line: 73)

    }
    fatalError("unreachable")
  }

  mutating func fill() -> Status {
    let shift = self.token
    let used = self.limit - self.token
    let free = Self.bufferSize - used

    // Error: No space. In the real world we can reallocate a larger buffer.
    if free < 1 {
      return .bigPacket
    }

    // Shift buffer contents, discarding everything up to the current lexeme.
    self.buffer.replaceSubrange(..<used, with: self.buffer[shift..<self.limit])
    self.limit  -= shift
    self.cursor -= shift
    self.marker -= shift
    self.token = 0

    // Fill free space at the end of buffer with new data.
    do {
      if let data = try self.file.read(upToCount: free) {
        self.buffer.replaceSubrange(self.limit..<(self.limit + data.count), with: data)
        self.limit += data.count
      }
    } catch {
      fatalError("cannot read from file: \(error.localizedDescription)")
    }
    self.buffer[self.limit] = 0  // append sentinel

    return .ready
  }

  enum Status {
    case end, ready, waiting, badPacket, bigPacket
  }
}

func test(_ packets: [StaticString],
    _ expectStatus: State.Status, _ expectNumbers: Int, _ expectWords: Int
) {
  // Create a "socket" (open the same file for reading and writing).
  let fname: String = "pipe"
  guard FileManager.default.createFile(atPath: fname, contents: nil),
      let fw = FileHandle(forWritingAtPath: fname) else {
    fatalError("cannot open '\(fname)'")
  }
  guard let fr = FileHandle(forReadingAtPath: fname) else {
    fatalError("cannot read file '\(fname)'")
  }

  defer {
    // Cleanup: remove input file.
    do {
      try fw.close()
      try fr.close()
      try FileManager.default.removeItem(atPath: fname)
    } catch {
      fatalError("error on file cleanup: \(error.localizedDescription)")
    }
  }

  var state = State(file: fr)

  // Main loop. The buffer contains incomplete data which appears packet by
  // packet. When the lexer needs more input it saves its internal state and
  // returns to the caller which should provide more input and resume lexing.
  var status: State.Status
  var send = 0, numCount = 0, wordCount = 0
  loop: while true {
    switch state.lex(numbers: &numCount, words: &wordCount) {
      case .end:
        log("done: got \(numCount) and \(wordCount) words")
        status = .end
        break loop
      case .waiting:
        log("waiting...")
        if send < packets.count {
          log("sent packet \(send)")
          packets[send].withUTF8Buffer {
            do {
              try fw.write(contentsOf: $0)
            } catch {
              fatalError("cannot write to \(fname): \(error.localizedDescription)")
            }
          }
          send += 1
        }
        status = state.fill()
        state.buffer.withUnsafeBytes {
          let buf = $0.bindMemory(to: CChar.self)
          log("queue: '\(String(utf8String: buf.baseAddress!) ?? "")'")
        }
        if status == .bigPacket {
          log("error: packet too big")
          break loop
        }
        assert(status == .ready)
      case .badPacket:
        log("error: ill-formed packet")
        status = .badPacket
        break loop
      default:
        fatalError("unreachable")
    }
  }

  // Check results.
  assert(status == expectStatus, "expected status `\(expectStatus)`, got `\(status)`")
  if status == .end {
    assert(numCount == expectNumbers,
      "expected number count \(expectNumbers), got \(numCount)")
    assert(wordCount == expectWords,
      "expected word count \(expectWords), got \(wordCount)")
  }
}

@main struct Program {
  static func main() {
    test([], .end, 0, 0)
    test([ " zero one", " ", "123", "4 tw", "o  456789" ], .end, 2, 3)
    test([ "zer0" ], .badPacket, -1, -1)
    test([ "looooooooooong;" ], .bigPacket, -1, -1)
  }
}
