Parsing with Blocks

Parsing with Blocks

CocoaHeads Hamburg

Transcript

  1. Parsing with Blocks CocoaHeads Hamburg Chris Eidhof

  2. Warning All of this is a bit crazy

  3. Building languages is magical

  4. stylesheet { multiplier = if(slide.onlyHeader, 2, 1), baseFontSize = 45

    * multiplier, splitBaseFontSize = 25, backgroundColor = colorScheme.backgroundColor, bodyFont = { family = "Apercu", color = colorScheme.textColor, size = baseFontSize } }
  5. DSLs → SQL → AutoLayout → NSPredicate → Regular Expressions

    → ActiveRecord
  6. DSLs External vs. Internal

  7. DSLs: External → SQL → CSS → NSPredicate format syntax

  8. 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
  9. V:[topField]-10-[bottomField] [flexibleButton(>=70,<=100)] |-[find]-[findNext]-[findField(>=20)]-|

  10. None
  11. Building an external DSL

  12. None
  13. Parsing 1. String → Stream of tokens 2. Stream of

    tokens → Syntax Tree 3. Interpret Syntax Tree
  14. Existing approaches → Hand-rolled parsers → Parser generators → ParseKit

    / CoreParse
  15. An alternative approach → Based on functional programming → Backtracking

    → Immutable objects
  16. The language

  17. 1 + 2 * 3 Expr ← Sum Sum ←

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

    Product '+' Product | Product Product ← Atom '*' Atom | Atom Atom ← Number
  19. (1 + 2) * 3

  20. (1 + 2) * 3 Expr ← Sum Sum ←

    Product '+' Product | Product Product ← Atom '*' Atom | Atom Atom ← Number | '(' Expr ')'
  21. Block Syntax returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; Source: http://goshdarnblocksyntax.com

  22. Live Coding

  23. A stylesheet language

  24. stylesheet { multiplier = if(slide.onlyHeader, 2, 1), baseFontSize = 45

    * multiplier, splitBaseFontSize = 25, backgroundColor = colorScheme.backgroundColor, bodyFont = { family = "Apercu", color = colorScheme.textColor, size = baseFontSize } }
  25. 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]; }); };
  26. 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]; }); };
  27. Rule mulExpression = infix(@"*", infix(@"/", functionCallExpression)); Rule addExpression = infix(@"+",

    infix(@"-", mulExpression));
  28. API

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

  30. @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
  31. State Internals @interface State () @property (nonatomic) NSArray *tokens; @property

    (nonatomic) NSUInteger tokenIndex; @end
  32. 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]; } };
  33. - (State *)next:(id)result { if (self.failed) return self; State *next

    = [self copy]; next.failed = NO; next.result = result; next.tokenIndex++; return next; }
  34. We created two DSLs → One for parsing (embedded) →

    One external https://github.com/chriseidhof/parsingwithblocks
  35. Thanks → @chriseidhof → http://www.objc.io → http://www.uikonf.com → http://www.decksetapp.com