Silver / Gold Challenge

#1

I think I have a working solution for the Gold Challenge, including Pseudowolks 10*4+9/3/3 giving 41. It is a recursive solution, which I think was the intention of the challenge.
The code has also my solution for the Silver Challenge. I’m not particularly happy with it, it is a bit kludgy but tells me the position in the original string and also the index in the parser :slight_smile: .

[code]//: Playground - noun: a place where people can play

import Cocoa

enum Token : CustomStringConvertible {
case Number(position: String.CharacterView.Index, value: Int)
case Plus(position: String.CharacterView.Index)
case Subtract(position: String.CharacterView.Index) // bronze
case Multiply(position: String.CharacterView.Index) // bronze
case Divide(position: String.CharacterView.Index) // bronze

var description : String {
    switch self {
    case .Number(let value):
        return String(value.value)
    case .Plus:
        return "+"
    case .Subtract:
        return "-"
    case .Divide:
        return "/"
    case .Multiply:
        return "*"
    }
}

}

class Lexer {
enum Error: ErrorType {
case InvalidCharacter(Character,String.CharacterView.Index)
}

let input: String.CharacterView
var position: String.CharacterView.Index

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 the end!")
    position++
}
func getNumber() -> Int {
    var value = 0
    while let nextCharacter = peek() {
        switch nextCharacter {
        case "0" ... "9":
            let digitValue = Int(String(nextCharacter))!
            value = 10 * value + digitValue
            advance()
        default:
           return value
        }
    }
    return value
}

func lex() throws -> [Token] {
    var tokens = [Token]()
    while let nextCharacter = peek() {
        
        let curPosition = position
        
        switch nextCharacter {
        case "0" ... "9":
            let value = getNumber()
            tokens.append(.Number(position: curPosition,value: value))
        case "+":
            tokens.append(.Plus(position: curPosition))
            advance()
        case "-":
            tokens.append(.Subtract(position: curPosition))
            advance()
        case "*":
            tokens.append(.Multiply(position: curPosition))
            advance()
        case "/":
            tokens.append(.Divide(position: curPosition))
            advance()
        case " ":
            advance()
        default:
            throw Error.InvalidCharacter(nextCharacter,curPosition)
        }
    }
    return tokens
}

}

class Parser {

enum Error: ErrorType {
    case UnexpectedEndOfInput
    case InvalidToken(token: Token,stringPosition: String.CharacterView.Index, position: Int)
}

let tokens: [Token]
var position = 0
init(tokens: [Token]) {
    self.tokens = tokens
}
func getNextToken() -> Token? {
    guard position < tokens.count else {
        return nil
    }
    return tokens[position++]
}

func getNumber() throws -> Int {
    guard let token = getNextToken() else {
        throw Error.UnexpectedEndOfInput
    }
    
    switch token {
    case .Number(let value):
        return value.value
    case .Plus(let value):
        throw Error.InvalidToken(token: token,stringPosition: value,position: position - 1)
    case .Subtract(let value):
        throw Error.InvalidToken(token: token,stringPosition: value, position: position - 1)
    case .Multiply(let value):
        throw Error.InvalidToken(token: token,stringPosition: value, position: position - 1)
    case .Divide(let value):
        throw Error.InvalidToken(token: token,stringPosition: value, position: position - 1)
    }
}
func parse( ) throws -> Int
{
    var value = try getNumber()
    while let token = getNextToken()
    {
        switch token{
        case .Multiply:
            let nextNumber = try getNumber()
            value *= nextNumber
        case .Divide:
            let nextNumber = try getNumber()
            
            value /= nextNumber
        case .Plus:
            let nextNumber = try parse()
            value += nextNumber
        case .Subtract:
            let nextNumber = try parse()
            value -= nextNumber
            
        case .Number(let value):
            throw Error.InvalidToken(token: token, stringPosition: value.position, position: position - 1)
        }
    }
    
    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){
    print("Input contained an invalid character at index \(character.1): \(character.0)")
}
catch Parser.Error.UnexpectedEndOfInput {
    print("Unexpected end of input during parsing")
}
catch Parser.Error.InvalidToken(let token) {
    print("Invalid token during parsing at index \(token.stringPosition) (parser index:\(token.position)) : \(token.token)")
}
catch {
    print("An error occurred: \(error)")
}

}

evaluate(β€œ10 + 3 + 5”) // 18
evaluate(β€œ10 + 5 - 3 - 1”) // 11
evaluate(β€œ1 + 3 + 7a + 8”) // error
evaluate(β€œ10 + 3 + + 7”) // error
evaluate(β€œ10 + 3 3 + 7”) // error
evaluate(β€œ10 * 3 * * + 5 * 3”) // error

evaluate(β€œ10 * 10 - 10 * 5”) // 50
evaluate(β€œ10 * 3 + 5 * 3”) // 45
evaluate(β€œ10 + 3 * 5 + 3”) // 28
evaluate(β€œ10 + 3 * 5 * 3”) // 55
evaluate(β€œ10 * 4 + 9 / 3 - 3”) // 40
evaluate(β€œ10 * 4 + 9 / 3 / 3”) // 41

[/code]

Gold Challenge - Recursive Solution Explanation