Super combinators

Post on 05-Apr-2017

226 views 0 download

transcript

SUPERCOMBINATORS A "SWIFTY" PARSER COMBINATOR

FRAMEWORK

SWIFTY

Use term with cautionStill being defined

Should mean "familiar"Can mean baggage !

WHY?

Why do this presentation?Why parser combinators?

Why SuperCombinators?

STRINGS

Two potential intended users of a string

1. !2. "

SNIPS

A lot of work on taking text for humans"Show me walking directions home"

Transform into something for machineschmod +x query.txt

ONE APPROACH

Context-free grammar

Assign semantic function to nonterminals

This is not easy to express cleanly in Swift

ONE SOLUTION

Define the semantic function as a String

Transform into a closure

More advanced ExpressibleByStringLiteral

PARSER COMBINATORS

Conceptually: typealias Parser<Value, Collection> = (Collection) -> (Value, Collection)?

Made to be composable

Unlike familiar Cocoa devices

EXISTING SOLUTIONS

A number of frameworks available already

All had downsides▸ Too many custom operators

▸ Memory leaks▸ Swift 2.2

EXISTING SOLUTIONS

Demo/teaching tool

Not for production

CLONE

First we cloned an existing solution

Migration ❤

Became intimately familiar with every line

REIMPLEMENT

What could be made better?▸ String-specific▸ Declarative API

▸ Memory-safe recursion

Other goodies

STRING

String can also be a "substring"let substring: String = text.substring(from: substringIndex)

Completely opaque

Captures text

Cheap to get substring with full String API

SWIFT TYPE CONVEYS SEMANTICS

Collection takes this to an extreme

Helps compiler and programmer

Pattern AND Parser<Value>

Pattern just traverses String

Parser extracts Value

Makes composition implicit while intuitive

OPERATORS

Distinction in types allows for simplification

Just || and & for almost all composition

mirrored by explicit .or(_:) and .and(_:) instance methods

EXTENSIONS

Distinction in types allows for useful extensionsExpressibleByStringLiteral

Swift 3.1:extension Parser where Value == String {}

RECURSIVE PARSERS

Reference cycles

Usually avoidable

Not always obvious how

RECURSIVE PARSERS

Use additional object with unowned reference back

lazily generate parsing function

take Parser -> Parser

EXAMPLElet digits = Pattern.characters(in: .decimalDigits)let int = digits.stringParser.map { Int($0)! }

let sum = Parser<Int>.recursive { sum in return (int & "+" & sum).map(+) || int}

print(sum.parse("1+2+3")!)// prints 6

THANK YOU!

github.com/snipsco/SuperCombinators

Sasha Lopoukhine

github.com/superlopuh@superlopuh