A solution for the bronze and silver challenges

My solution consists to keep track of the position of each token in the input string. To do so I added a struct containing the Token type and an Int. Then the Lexer and Parser classes work with an Array made of this new TokenStruct struct. The error thrown by the Parser contains both the faulty token and its position in the input string.

import Cocoa

enum Token {
    case number(Int)
    case plus
    case minus
}
struct TokenStruct {
    var token: Token
    var posit: Int
}
class Lexer {
    enum Error: Swift.Error {
        case invalidCharacter(Character, pos:Int)
    }
    let input: String.CharacterView
    var position: String.CharacterView.Index
    var indexes = [Int]()

    
    init(input: String) {
        self.input = input.characters
        self.position = self.input.startIndex
    }
    func peek() -> Character? {
        guard position < input.endIndex else {
            return nil
        }
        return input[position]
    }
    func advance() {
        assert(position < input.endIndex, "Cannot advance past endIndex!")
        position = input.index(after: position)
    }
    func lex() throws -> [TokenStruct] {
        var tokens = [TokenStruct]()
        
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0"..."9":
                // Start of a number - need to grab the rest 
                let posChar = input.distance(from: input.startIndex, to: position)
                let value = getNumber()
                tokens.append(TokenStruct(token: .number(value), posit: posChar))
            case "+":
                let posChar = input.distance(from: input.startIndex, to: position)
                tokens.append(TokenStruct(token: .plus, posit: posChar))
                advance()
            case "-":
                let posChar = input.distance(from: input.startIndex, to: position)
                tokens.append(TokenStruct(token: .minus, posit: posChar))
                advance()
            case " ":
                // Just advance to ignore spaces advance()
                advance()
            default:
                // Something unexpected - need to send back an error
                let distanceToPosition = input.distance(from: input.startIndex, to: position)
                throw Lexer.Error.invalidCharacter(nextCharacter, pos: distanceToPosition)
            }
        }
        return tokens
    }
    func getNumber() -> Int {
        var value = 0
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0" ... "9": // Another digit - add it into value 
                let digitValue = Int(String(nextCharacter))!
                value = 10*value + digitValue
                advance()
            default: // A nondigit - go back to regular lexing 
                return value
            }
        }
        return value
    }
}
class Parser {
    enum Error: Swift.Error {
        case unexpectedEndOfInput
        case invalidToken(TokenStruct)
    }
    let tokens: [TokenStruct]
    var position = 0
    init(tokens: [TokenStruct]) {
        self.tokens = tokens
    }
    func getNextToken() -> TokenStruct? {
        guard position < tokens.count else {
            return nil
        }
        let token = tokens[position]
        position += 1
        return token
    }
    func getNumber() throws -> Int {
        guard let token = getNextToken() else {
            throw Parser.Error.unexpectedEndOfInput
        }
        switch token.token {
        case .number(let value):
            return value
        case .plus, .minus:
            throw Parser.Error.invalidToken(token)
        }
    }
    func parse() throws -> Int { // Require a number first 
        var value = try getNumber()
        while let token = getNextToken() {
            switch token.token { // Getting a plus after a number is legal
            case .plus: // After a plus, we must get another number 
                let nextNumber = try getNumber()
                value += nextNumber // Getting a number after a number is not legal
            case .minus:
                let nextNumber = try getNumber()
                value -= nextNumber
            case .number:
                throw Parser.Error.invalidToken(token)
            }
        }
        return value
    }
}
func evaluate(_ input: String) {
    print(" Evaluating: \(input)")
    let lexer = Lexer(input: input)
    do {
        let tokens = try lexer.lex()
//        print(" Lexer output: \(tokens)")
        let parser = Parser(tokens: tokens)
        let result = try parser.parse()
        print(" Parser output: \( result)")
    } catch Lexer.Error.invalidCharacter(let character, let posErr) {
        print(" Input contained an invalid character at index \(posErr) : \(character)")
    } catch Parser.Error.unexpectedEndOfInput {
        print(" Unexpected end of input during parsing")
    } catch Parser.Error.invalidToken(let token) {
        print(" Invalid token \(token.token) during parsing at index: \(token.posit)")
    } catch {
       print(" An error occurred: \(error)")
    }
}
evaluate("10 + 3 3 + 7")