Parsing(Swi+ly
Parsing
Lexing'(Tokenizing)
Parsing((Extrac.ng(meaning)
What%is%the%Type%of%a%parser?
Parsing(an(Int
String'(>'Int
String'(>'(Int,'remainder:'String)
String'(>'(Int,'remainder:'String)?
<ValueType>+String+2>+(ValueType,+remainder:+
String)?
struct Parser<ValueType> {
let apply:(String) -> (ValueType, String)?
}
struct Parser<ValueType> {
typealias Function = (String) -> (ValueType, String)?
let apply:Function
init(apply: Function) {
self.apply = apply
}
}
Building(Blocks
Parser.result()
Parser.zero()
Parser.item()
Parser.result()
static func result(value:ValueType) -> Parser {
return Parser { (input) in
return (value, remainder:input)
}
}
Parser.zero()
static func zero() -> Parser {
return Parser { (input) in
return nil
}
}
Parser.item()
static func item() -> Parser<Character> {
return Parser<Character> { (input) in
let firstIndex = input.startIndex
if firstIndex == input.endIndex {
return nil
} else {
return (input[firstIndex], remainder: String(dropFirst(input)))
}
}
}
Let$Our$Parsers$Combine!
bind
func bind<T, U>(parserT:Parser<T>, lift:(T -> Parser<U>)) -> Parser<U> {
return Parser<U> { (input) in
if let (theT, theRemainder) = parserT.apply(input) {
return lift(theT).apply(theRemainder)
} else {
return nil
}
}
}
sa#sfy
func satisfy(predicate:(Character -> Bool)) -> Parser<Character> {
return bind(Parser<Character>.item()) { theChar in
if predicate(theChar) {
return Parser.result(theChar)
} else {
return Parser.zero()
}
}
}
char
func char(x:Character) -> Parser<Character> {
return satisfy { $0 == x }
}
func charIn<CharSeq: SequenceType where
CharSeq.Generator.Element == Character>
(x:CharSeq) -> Parser<Character> {
return satisfy { Set(x).contains($0) }
}
let digit:Parser<Character> = satisfy { "0"..."9" ~= $0 }
let lower:Parser<Character> = satisfy { "a"..."z" ~= $0 }
let upper:Parser<Character> = satisfy { "A"..."Z" ~= $0 }
I"Said""Let"Our"Parsers"Combine!"
let nsParser:Parser<String> = bind(char("N")) { (theLetterN) in
bind(char("S")) { (theLetterS) in
Parser<String>.result(String([theLetterN, theLetterS]))
}
}
nsParser.apply("NS")
nsParser.apply("AB")
a"small"detour
extension Optional {
internal func bind<U>(f: T -> U?) -> U? {
if let x = self {
return f(x)
} else {
return nil
}
}
}
func headAndTail(input:String) -> (head:Character, tail:String)? {
return first(input).bind { head in
return (head:head, tail:String(dropFirst(input)))
}
}
Words!
func word(x:String) -> Parser<String> {
if let found = headAndTail(x) {
return bind(char(found.head)) { (a:Character) in
return bind(word(found.tail)) { (b:String) in
return Parser<String>.result(x)
}
}
} else {
return Parser<String>.result(x)
}
}
Choice
func choice<T>(lhs:Parser<T>, rhs:Parser<T>) -> Parser<T> {
return Parser { (input) in
if let leftParse = lhs.apply(input) {
return leftParse
} else {
return rhs.apply(input)
}
}
}
Parse&you&a&Monad
Monadic(Parser(Combinators
Graham&Hu)on&and&Erik&Meijer
• h#p://www.cs.no#.ac.uk/~gmh/monparsing.pdf)
Parsec:(Direct(Style(Monadic(Parser(Combinators(For(The(Real(
World
Daan$Leijen
• h#p://research.microso0.com/en2us/um/people/daan/
download/papers/parsec2paper.pdf
Examples
• Rob%Rix%(%Madness%(%h0ps://github.com/robrix/Madness
• Parsec%

Parsing swiftly-Cocoaheads-2015-02-12