/* Generated by re2swift */
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 1)
// re2swift $INPUT -o $OUTPUT --api generic --tags
import Foundation

struct SemVer: Equatable { var major: Int, minor: Int, patch: Int }

struct Input {
  static let bufferSize = 4095
  static let tagNone = -1

  var yyinput  = ContiguousArray<UInt8>(repeating: 0, count: Self.bufferSize + 1)
  var yylimit  = Self.bufferSize
  var yycursor = Self.bufferSize
  var yymarker = Self.bufferSize
  var token    = Self.bufferSize
  // Intermediate tag variables must be part of the lexer state passed to YYFILL.
  // They don't correspond to tags and should be autogenerated by re2c.
  
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 21)
  var yyt1 = Self.tagNone
  var yyt2 = Self.tagNone
  var yyt3 = Self.tagNone
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 17)

  var eof = false

  let file: FileHandle
}

extension Input {
  mutating func lex() -> [SemVer]? {
    var semVers = [SemVer]()
    semVers.reserveCapacity(Self.bufferSize)

    // Final tag variables available in semantic action.
    
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 39)
    var t1: Int
    var t2: Int
    var t3: Int
    var t4: Int
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 29)


    parse: while true {
      self.token = self.yycursor
      
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 50)
  var yych: UInt8 = 0
  var yystate: UInt = 0
  yyl: while true {
    switch yystate {
      case 0:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x30...0x39:
            self.yycursor += 1
            yystate = 3
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 0
                continue yyl
              }
              yystate = 11
              continue yyl
            }
            self.yycursor += 1
            yystate = 1
            continue yyl
        }
      case 1:
        yystate = 2
        continue yyl
      case 2:
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 51)
        return nil
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 81)
      case 3:
        self.yymarker = self.yycursor
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x2E:
            self.yycursor += 1
            yystate = 4
            continue yyl
          case 0x30...0x39:
            self.yycursor += 1
            yystate = 6
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 3
                continue yyl
              }
            }
            yystate = 2
            continue yyl
        }
      case 4:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x30...0x39:
            self.yyt1 = self.yycursor
            self.yycursor += 1
            yystate = 7
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 4
                continue yyl
              }
            }
            yystate = 5
            continue yyl
        }
      case 5:
        self.yycursor = self.yymarker
        yystate = 2
        continue yyl
      case 6:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x2E:
            self.yycursor += 1
            yystate = 4
            continue yyl
          case 0x30...0x39:
            self.yycursor += 1
            yystate = 6
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 6
                continue yyl
              }
            }
            yystate = 5
            continue yyl
        }
      case 7:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x0A:
            self.yyt2 = self.yycursor
            self.yyt3 = -1
            self.yycursor += 1
            yystate = 8
            continue yyl
          case 0x2E:
            self.yyt2 = self.yycursor
            self.yycursor += 1
            yystate = 9
            continue yyl
          case 0x30...0x39:
            self.yycursor += 1
            yystate = 7
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 7
                continue yyl
              }
            }
            yystate = 5
            continue yyl
        }
      case 8:
        t2 = self.yyt1
        t3 = self.yyt2
        t4 = self.yyt3
        t1 = self.yyt1
                t1 -= 1
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 42)
        
          semVers.append(SemVer(
            major: self.s2n(self.token..<t1),
            minor: self.s2n(t2..<t3),
            patch: t4 != Self.tagNone ? self.s2n(t4..<(self.yycursor - 1)) : 0
          ))
          continue parse

#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 190)
      case 9:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x30...0x39:
            self.yyt3 = self.yycursor
            self.yycursor += 1
            yystate = 10
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 9
                continue yyl
              }
            }
            yystate = 5
            continue yyl
        }
      case 10:
        yych = self.yyinput[self.yycursor]
        switch yych {
          case 0x0A:
            self.yycursor += 1
            yystate = 8
            continue yyl
          case 0x30...0x39:
            self.yycursor += 1
            yystate = 10
            continue yyl
          default:
            if self.yylimit <= self.yycursor {
              if self.fill() == .ok {
                yystate = 10
                continue yyl
              }
            }
            yystate = 5
            continue yyl
        }
      case 11:
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 50)
        return semVers
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 233)
      default: fatalError("internal lexer error")
    }
  }
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 52)

    }
  }

  func s2n(_ range: Range<Int>) -> Int {
    self.yyinput[range].reduce(0) { accum, digit in
      accum * 10 + Int(digit - UInt8(ascii: "0"))
    }
  }

  mutating func fill() -> FillStatus {
    guard !self.eof else {
      return .eof
    }

    let shift = self.token
    let used = self.yylimit - self.token
    let free = Self.bufferSize - used

    // Error: Lexeme too long. In the real world we could reallocate a larger buffer.
    guard self.token >= 1 else {
      return .longLexeme
    }

    // Shift buffer contents, discarding everything up to the current token.
    self.yyinput.replaceSubrange(..<used, with: self.yyinput[shift..<self.yylimit])
    self.yylimit   -= shift
    self.yycursor  -= shift
    self.yymarker &-= shift  // May underflow is marker is unused
    self.token = 0
    // Tag variables need to be shifted like other input positions. The check
    // for `tagNone` is only needed if some tags are nested inside of alternative
    // or repetition, so that they can have `tagNone` value.
    
#sourceLocation(file: "swift/submatch/01_stags_fill.swift", line: 272)
    if self.yyt1 != Self.tagNone { self.yyt1 -= shift }
    if self.yyt2 != Self.tagNone { self.yyt2 -= shift }
    if self.yyt3 != Self.tagNone { self.yyt3 -= shift }
#sourceLocation(file: "swift/submatch/01_stags_fill.re", line: 85)


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

    return .ok
  }

  enum FillStatus {
    case ok, eof, longLexeme
  }
}

extension SemVer: CustomStringConvertible {
  var description: String { "\(self.major).\(self.minor).\(self.patch)" }
}

let fileName = "input"
let semVer = SemVer(major: 1, minor: 22, patch: 333)
let expect = [SemVer](repeating: semVer, count: Input.bufferSize)

// Prepare input file (make sure it exceeds buffer size).
guard FileManager.default.createFile(
    atPath: fileName,
    contents: Data(String(repeating: "\(semVer)\n", count: Input.bufferSize).utf8)
) else {
  fatalError("failed to write file \"\(fileName)\"")
}

// Reopen input file for reading.
guard let file = FileHandle(forReadingAtPath: fileName) else {
  throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue)
}

// Initialize lexer state. Buffer is set to zero, triggering YYFILL.
var `in` = Input(file: file)

// Run the lexer and check the results.
guard let actual = `in`.lex() else {
  fatalError("parser error")
}

assert(actual == expect)

// Cleanup: remove input file.
try file.close()
try FileManager.default.removeItem(atPath: fileName)
