Heres’ the code:
Blockquote
import Cocoa
enum Token : CustomStringConvertible {
case number(Int)
case plus
case minus
case multiplication
case division
var description: String {
switch self {
case .number(let n):
return "Number: \(n)"
case .plus:
return "Symbol: +"
case .minus:
return "Symbol: -"
case .multiplication:
return "Symbol: *"
case .division:
return "Symbol: /"
};
}
}
class Lexer {
func getNumber() → Int {
var value = 0
while let nextCharacter = peek() {
switch nextCharacter {
case "0"..."9": // Another digit - add to the value
let digitValue = Int(String(nextCharacter))!
value = 10*value + digitValue
advance()
default:
// Something unexpected - need to send back an error
return value
}
}
return value
}
enum Error : Swift.Error {
case invalidCharacter(Character)
case unexpectedEndOfInput
}
let input: String
var position: String.Index
init(input: String) {
self.input = input
self.position = 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 -> [Token] {
var tokens = [Token]()
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 "+":
tokens.append(.plus)
advance()
case "-":
tokens.append(.minus)
advance()
case "*":
tokens.append(.multiplication)
advance()
case "/":
tokens.append(.division)
advance()
case " ":
// Just advance to ignore spaces
advance()
default:
// Something unexpected - need to send back an error
throw Lexer.Error.invalidCharacter(nextCharacter)
}
}
return tokens
}
}
class Parser {
enum Error : Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
let tokens: [Token]
var position = 0
init(tokens: [Token]) {
self.tokens = tokens
}
func getNextToken() -> Token? {
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 {
case .number(let value):
return value
case .plus:
throw Parser.Error.invalidToken(token)
case .minus:
throw Parser.Error.invalidToken(token)
case .multiplication:
throw Parser.Error.invalidToken(token)
case .division:
throw Parser.Error.invalidToken(token)
}
}
func parse() throws -> Int {
// Require a number first
var value = try getNumber()
var previousNumber = try getNumber()
while let token = getNextToken() {
var previousToken = token
switch 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:
// After a minus, we must get another number
let nextNumber = try getNumber()
value -= nextNumber
case .multiplication:
// After a multiplication, we must get a number
let nextNumber = try getNumber()
previousNumber = nextNumber
previousToken = token
case .division:
// After a division, we must get a nmuber
let nextNumber = try getNumber()
previousNumber = nextNumber
previousToken = token
case .number(let nextnumber):
switch previousToken {
case .multiplication:
<#code#>
default:
<#code#>
}
}
}
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 position = lexer.position
let distanceToPosition = input.distance(from: input.startIndex, to: position)
print("Input contained an invalid input character at \(distanceToPosition): \(character)")
} catch Parser.Error.unexpectedEndOfInput {
print("Unexpected end of input during parsing")
} catch Parser.Error.invalidToken(let token) {
let position = lexer.position
let distanceToPosition = input.distance(from: input.startIndex, to: position)
print("Invalid token during parsing at \(distanceToPosition): \(token)")
} catch {
print("An error occurred: \(error)")
}
}
evaluate(“10 * 3 + 5 * 3”)
evaluate(“10 + 3 * 5 + 3”)
evaluate(“10 + 3 * 5 * 3”)
I’m using Xcode version 5.3.