Listing 20.14 Results Not Quite Right


#1

Hi,

I have written all of the code in chapter 20 as far as listing 20.14. However, my output is slightly different that the book’s. This is what I get:

Evaluating: 10 + 3 + 5
Lexer output: [__lldb_expr_175.Token.number(10), __lldb_expr_175.Token.plus, __lldb_expr_175.Token.number(3), __lldb_expr_175.Token.plus, __lldb_expr_175.Token.number(5)]

Evaluating: 1 + 2 + abcdefg
Input contained an invalid character: a

I am confused at these: __lldb_expr_175.

Why does my output show these? I’ve copied the book’s code twice to make sure I got it right, but no luck. This is my entire code (as seen in the book):

import Cocoa

enum Token {
case number(Int)
case plus
}

class Lexer {
// properties and initialization (pass in a Character type)
enum Error: Swift.Error {
case invalidCharacter(Character)
}
let input: String.CharacterView
var position: String.CharacterView.Index

init(input: String) {
    self.input = input.characters
    self.position = self.input.startIndex
}

// check if there is a next character, if not already at the last one
func peek() -> Character? {
    guard position < input.endIndex else {
        return nil
    }
    return input[position]
}

// advance to the next character, if not already at the last one
func advance() {
    // catch a 'nonrecoverable' error
    assert(position < input.endIndex, "Cannot advance past endIndex!")
    // advance if the conditions above were met
    position = input.index(after: position)
}

// get the sequence of the numbers
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
}

// implement the lexing algorithm —— 'throws' means the function can fail, which allows code to print out an error message
func lex() throws -> [Token] {
    var tokens = [Token]()
    
    // peek() is a function that returns a value, thus nextCharacter is equal to peek()
    while let nextCharacter = peek() {
        switch nextCharacter {
        case "0" ... "9":
            // Start of a number —— need to grab the rest
            let value = getNumber()
            tokens.append(.number(value))
            
        case "+":
            // append '+' to the .plus case of the tokens array
            tokens.append(.plus)
            advance()
            
        case " ":
            // Just advance to ignore spaces
            advance()
            
        default:
            // Something unexpected —— need to send back an error
            throw Lexer.Error.invalidCharacter(nextCharacter)
        }
    }
    
    return tokens
}

}

// test the lexing algorithm
func evaluate(_ input: String) {
print(“Evaluating: (input)”)
let lexer = Lexer(input: input)

do {
    let tokens = try lexer.lex()
    print("Lexer output: \(tokens)")
} catch Lexer.Error.invalidCharacter(let character) {
    print("Input contained an invalid character: \(character)")
} catch {
    print("An error occured: \(error)")
}

}

evaluate(“10 + 3 + 5”)
print("")
evaluate(“1 + 2 + abcdefg”)

Any help is appreciated.
—Husain


#2

This is what I discovered just now:

What I explained above showed up when using Xcode 8.3.3. However, when I used the same code on my older Mac, which runs Xcode 8.2.1, the results matched those of the book.

Does that mean something has changed between the two Xcode versions? And what is it?


#3

Hi Husain,

It appears the default behavior for printing enums in playgrounds varied slightly in this version of Xcode.

If you were to run your code as a command line application in Xcode, rather than a playground, you would instead see the output as shown in the book.

A better solution though so we have consistent output, would be to add a CustomStringConvertible like we did in the last chapter on Protocols.

enum Token: CustomStringConvertible {
    case number(Int)
    case plus

    var description: String {
        switch self {
        case .number(let value):
            return "Number: \(value)"
        case .plus:
            return "Symbol: +"
        }
    }
}

Now the output will from the lexer will look like this:

Lexer output: [Number: 10, Symbol: +, Number: 3, Symbol: +, Number: 5]

Playgrounds also allow you to define how your custom types are represented in the tool-bar when you select the QuickLook item. We can add a second protocol conformance to our Token and return the same description so that our text is displayed the same everywhere.

enum Token: CustomStringConvertible, CustomPlaygroundQuickLookable {
    case number(Int)
    case plus
    var description: String {
        switch self {
        case .number(let value):
            return "Number: \(value)"
        case .plus:
            return "Symbol: +"
        }
    }

    var customPlaygroundQuickLook: PlaygroundQuickLook {
        return .text(description)
    }
}

Hope this helps clear up why it looks different in this version of Xcode. Thanks


#4

Hi Robert,

Thank you very much for clearing things up. Now it all makes sense and I can understand what’s happening.

—Husain


#5

I had a similar issue in Xcode 9.1 ("__lldb_expr_44" in my playground output). This solved the issue. Thanks!