Slide 1

Slide 1 text

Parsing with Blocks CocoaHeads Hamburg Chris Eidhof

Slide 2

Slide 2 text

Warning All of this is a bit crazy

Slide 3

Slide 3 text

Building languages is magical

Slide 4

Slide 4 text

stylesheet { multiplier = if(slide.onlyHeader, 2, 1), baseFontSize = 45 * multiplier, splitBaseFontSize = 25, backgroundColor = colorScheme.backgroundColor, bodyFont = { family = "Apercu", color = colorScheme.textColor, size = baseFontSize } }

Slide 5

Slide 5 text

DSLs → SQL → AutoLayout → NSPredicate → Regular Expressions → ActiveRecord

Slide 6

Slide 6 text

DSLs External vs. Internal

Slide 7

Slide 7 text

DSLs: External → SQL → CSS → NSPredicate format syntax

Slide 8

Slide 8 text

DSLs: Internal [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(superview.mas_top).with.offset(padding.top); make.left.equalTo(superview.mas_left).with.offset(padding.left); make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); make.right.equalTo(superview.mas_right).with.offset(-padding.right); }] Source: Masonry

Slide 9

Slide 9 text

V:[topField]-10-[bottomField] [flexibleButton(>=70,<=100)] |-[find]-[findNext]-[findField(>=20)]-|

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Building an external DSL

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Parsing 1. String → Stream of tokens 2. Stream of tokens → Syntax Tree 3. Interpret Syntax Tree

Slide 14

Slide 14 text

Existing approaches → Hand-rolled parsers → Parser generators → ParseKit / CoreParse

Slide 15

Slide 15 text

An alternative approach → Based on functional programming → Backtracking → Immutable objects

Slide 16

Slide 16 text

The language

Slide 17

Slide 17 text

1 + 2 * 3 Expr ← Sum Sum ← Product '+' Product Product ← Atom '*' Atom Atom ← Number

Slide 18

Slide 18 text

1 + 2 * 3 Expr ← Sum Sum ← Product '+' Product | Product Product ← Atom '*' Atom | Atom Atom ← Number

Slide 19

Slide 19 text

(1 + 2) * 3

Slide 20

Slide 20 text

(1 + 2) * 3 Expr ← Sum Sum ← Product '+' Product | Product Product ← Atom '*' Atom | Atom Atom ← Number | '(' Expr ')'

Slide 21

Slide 21 text

Block Syntax returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; Source: http://goshdarnblocksyntax.com

Slide 22

Slide 22 text

Live Coding

Slide 23

Slide 23 text

A stylesheet language

Slide 24

Slide 24 text

stylesheet { multiplier = if(slide.onlyHeader, 2, 1), baseFontSize = 45 * multiplier, splitBaseFontSize = 25, backgroundColor = colorScheme.backgroundColor, bodyFont = { family = "Apercu", color = colorScheme.textColor, size = baseFontSize } }

Slide 25

Slide 25 text

Rule stylesheet = ^(State *p) { __block NSArray *functions = nil; return p.identifier().token(@"{"). manySepBy(method, comma).bind(to(functions)). token(@"}").yield(^id { return [[StylesheetObject alloc] initWithFunctions:functions]; }); };

Slide 26

Slide 26 text

Rule method = ^(State * p) { __block NSString* name = nil; __block id body= nil; __block NSArray *parameters = nil; return p.identifier().bind(to(name)). optional(parameterList).bind(to(parameters)). token(@"="). rule(self.expression).bind(to(body)). yield(^id { return [[StylesheetFunction alloc] initWithName:name body:body parameters:parameters]; }); };

Slide 27

Slide 27 text

Rule mulExpression = infix(@"*", infix(@"/", functionCallExpression)); Rule addExpression = infix(@"+", infix(@"-", mulExpression));

Slide 28

Slide 28 text

API

Slide 29

Slide 29 text

typedef State *(^Rule)(State *p);

Slide 30

Slide 30 text

@interface State : NSObject @property (nonatomic, strong) id result; @property (nonatomic) BOOL failed; @property (nonatomic) NSString *errorMessage; @property (nonatomic) State *(^token)(NSString *); @property (nonatomic) State *(^identifier)(); @property (nonatomic) State *(^yield)(id (^)()); @property (nonatomic) State *(^bind)(void(^)(id result)); @property (nonatomic) State *(^oneOf)(NSArray *); @property (nonatomic) State *(^optional)(Rule); @property (nonatomic) State *(^eof)(); @property (nonatomic) State *(^rule)(Rule); @end

Slide 31

Slide 31 text

State Internals @interface State () @property (nonatomic) NSArray *tokens; @property (nonatomic) NSUInteger tokenIndex; @end

Slide 32

Slide 32 text

self.token = ^(NSString* token) { if (self.failed) return self; NSString* peek = self.peek; if ([token isEqual:peek]) { return [self next:peek]; } else { NSString* msg = [NSString stringWithFormat:@"Expected '%@', saw '%@'", token, peek]; return [self fail:msg]; } };

Slide 33

Slide 33 text

- (State *)next:(id)result { if (self.failed) return self; State *next = [self copy]; next.failed = NO; next.result = result; next.tokenIndex++; return next; }

Slide 34

Slide 34 text

We created two DSLs → One for parsing (embedded) → One external https://github.com/chriseidhof/parsingwithblocks

Slide 35

Slide 35 text

Thanks → @chriseidhof → http://www.objc.io → http://www.uikonf.com → http://www.decksetapp.com