Successfully reported this slideshow.
Your SlideShare is downloading. ×

Monadic parsers in C++

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 58 Ad
Advertisement

More Related Content

Slideshows for you (20)

Similar to Monadic parsers in C++ (20)

Advertisement

More from Alexander Granin (20)

Advertisement

Monadic parsers in C++

  1. 1. Monadic parsers Alexander Granin graninas@gmail.com C++ Russia 2019, Moscow 1
  2. 2. С++ Siberia 2019, Keynote The Present and the Future of Functional Programming in C++ С++ Russia 2018 Functional Approach to Software Transactional Memory in C++ С++ Russia 2016 Functional “Life”: Parallel Cellular Automata and Comonads in C++ С++ Siberia 2015 Functional Microscope: Lenses in C++ С++ User Group 2014 Functional and Declarative Design in C++ 3
  3. 3. struct Presentation { Introduction Parsing and Backus-Naur form boost::spirit (qi) cpp_parsec_free Monadic parsers State & context-sensitive grammars Conclusion }; 4
  4. 4. age integer first name string last name string salary float employee {35, “Jane”, “Street”, 54321} struct Employee { int age; string firstName; string lastName; double salary; }; Parsing 5
  5. 5. employee ::= "employee" spaces "{" spaces age "," spaces firstName "," spaces lastName "," spaces salary spaces "}" Backus-Naur Form quotedString ::= '"' string '"' age ::= integer firstName ::= quotedString lastName ::= quotedString salary ::= double 6
  6. 6. boost::spirit (Qi) 7
  7. 7. quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; start %= lit("employee") >> '{' >> int_ >> ',' >> quoted_string >> ',' >> quoted_string >> ',' >> double_ >> '}'; } 8
  8. 8. quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; start %= lit("employee") >> '{' >> int_ >> ',' >> quoted_string >> ',' >> quoted_string >> ',' >> double_ >> '}'; } qi::rule<Iterator, string(), ascii::space_type> quoted_string; qi::rule<Iterator, employee(), ascii::space_type> start; 9
  9. 9. template <typename Iterator> struct employee_parser : qi::grammar<Iterator, employee(), ascii::space_type>{ employee_parser() : employee_parser::base_type(start) { quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; start %= lit("employee") >> '{' >> int_ >> ',' >> quoted_string >> ',' >> quoted_string >> ',' >> double_ >> '}'; } qi::rule<Iterator, string(), ascii::space_type> quoted_string; qi::rule<Iterator, employee(), ascii::space_type> start; }; 10
  10. 10. template <typename Iterator> struct employee_parser : qi::grammar<Iterator, employee(), ascii::space_type>{ employee_parser() : employee_parser::base_type(start) { quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; start %= lit("employee") >> '{' >> int_ >> ',' >> quoted_string >> ',' >> quoted_string >> ',' >> double_ >> '}'; } qi::rule<Iterator, string(), ascii::space_type> quoted_string; qi::rule<Iterator, employee(), ascii::space_type> start; }; 11
  11. 11. 12
  12. 12. cpp_parsec_free 13
  13. 13. ParserT<Employee> employee = app<Employee, int, string, string, double>( mkEmployee, prefix >> intP, comma >> quotedString, comma >> quotedString, comma >> (doubleP << postfix)); 14
  14. 14. ParserT<Employee> employee = app<Employee, int, string, string, double>( mkEmployee, prefix >> intP, comma >> quotedString, comma >> quotedString, comma >> (doubleP << postfix)); auto prefix = lit("employee") >> between(spaces, symbol('{')); auto postfix = between(spaces, symbol('}')); auto comma = symbol(',') >> spaces; auto mkEmployee = [](int a, const string& sn, const string& fn, double s) { return Employee {a, sn, fn, s}; }; 15
  15. 15. auto s = "employee {35, “Jane”, “Street”, 54321}"; ParserResult<Employee> result = parse(employee, s); 16
  16. 16. auto s = "employee {35, “Jane”, “Street”, 54321}"; ParserResult<Employee> result = parse(employee, s); if (isLeft(result)) { std::cout << "Parse error: " << getError(result).message; } else { Employee employee = getParsed(result); } 17
  17. 17. Parser combinators 18
  18. 18. ParserT<int> intP; // integer number ParserT<double> doubleP; // double precision float ParserT<Char> digit; // 0..9 ParserT<Char> lower; // a..z ParserT<Char> upper; // A..Z ParserT<Char> letter; // a..z A..Z ParserT<Char> alphaNum; // 0..9 a..z A..Z ParserT<Char> symbol(Char ch); // symbol ParserT<string> lit(const string&); // string Basic parsers 19
  19. 19. template <typename A, typename B> ParserT<A> fst(const ParserT<A>& p1, const ParserT<B>& p2); template <typename A, typename B> ParserT<B> snd(const ParserT<A>& p1, const ParserT<B>& p2); Parser combinators 20
  20. 20. template <typename A, typename B> ParserT<A> fst(const ParserT<A>& p1, const ParserT<B>& p2); template <typename A, typename B> ParserT<B> snd(const ParserT<A>& p1, const ParserT<B>& p2); // fst template <typename A, typename B> ParserT<A> operator<< (const ParserT<A>& l, const ParserT<B>& r); // snd template <typename A, typename B> ParserT<B> operator>> (const ParserT<A>& l, const ParserT<B>& r); Parser combinators 21
  21. 21. ParserT<string> helloWorld = snd(lit("Hello"), snd(symbol(' '), lit("world"))); ParserT<string> helloWorld = lit("Hello") >> symbol(' ') >> lit("world"); Parsing “Hello world” 22
  22. 22. template <typename A> ParserT<Many<A>> many(const ParserT<A>& p); // 0 or many template <typename A> ParserT<Many<A>> many1(const ParserT<A>& p); // 1 or many template <typename A, typename B> ParserT<B> between(const ParserT<A>& bracketP, // between const ParserT<B>& p); More combinators 23
  23. 23. template <typename A> ParserT<A> alt(const ParserT<A>& l, const ParserT<A>& r); template <typename A> ParserT<A> operator| (const ParserT<A>& l, const ParserT<A>& r); Alternative 24
  24. 24. template <typename A> ParserT<A> alt(const ParserT<A>& l, const ParserT<A>& r); template <typename A> ParserT<A> operator| (const ParserT<A>& l, const ParserT<A>& r); ParserT<string> boolean = alt(lit("true"), lit("false")); ParserT<string> currency = lit("EUR") | lit("USD") | lit("GBP"); Alternative 25
  25. 25. Monadic parsers 26
  26. 26. Functor fmap :: (A -> B) -> ParserT<A> -> ParserT<B> Applicative app :: (A -> B -> C) -> ParserT<A> -> ParserT<B> -> ParserT<C> Monad bind :: ParserT<A> -> (A -> ParserT<B>) 27
  27. 27. fmap :: (A -> B) -> ParserT<A> -> ParserT<B> template <typename A, typename B> ParserT<B> fmap(const function<B(A)>& f, const ParserT<A>& p); Functor 28
  28. 28. fmap :: (A -> B) -> ParserT<A> -> ParserT<B> template <typename A, typename B> ParserT<B> fmap(const function<B(A)>& f, const ParserT<A>& p); ParserT<string> strBoolean = lit("true") | lit("false"); Functor 29
  29. 29. fmap :: (A -> B) -> ParserT<A> -> ParserT<B> template <typename A, typename B> ParserT<B> fmap(const function<B(A)>& f, const ParserT<A>& p); ParserT<string> strBoolean = lit("true") | lit("false"); ParserT<bool> boolean = fmap<string, bool>(f, strBoolean); function<bool(string)> f = [](const string& s) { return s == "true"; }; Functor 30
  30. 30. Functor fmap :: (A -> B) -> ParserT<A> -> ParserT<B> Applicative app :: (A -> B -> C) -> ParserT<A> -> ParserT<B> -> ParserT<C> Monad bind :: ParserT<A> -> (A -> ParserT<B>) 31
  31. 31. app :: (A -> B -> C) -> ParserT<A> -> ParserT<B> -> ParserT<C> template <typename A, typename B, typename C> ParserT<C> app(const function<C(A, B)>& f, const ParserT<A>& a, const ParserT<B>& b); Applicative 32
  32. 32. app :: (A -> B -> C) -> ParserT<A> -> ParserT<B> -> ParserT<C> template <typename A, typename B, typename C> ParserT<C> app(const function<C(A, B)>& f, const ParserT<A>& a, const ParserT<B>& b); ParserT<string> helloUsername = app<string, string>( mkHello, letters, spaces >> letters); string mkHello = [](const string& firstName, const string& secondName) { return string("Hello, ") + firstName + " " + secondName + "!"; }; Applicative 33
  33. 33. ParserT<Value> value = app(mkValue, intP | doubleP); Applicative is combinable 34
  34. 34. ParserT<Value> value = app(mkValue, intP | doubleP); ParserT<BinOp> binOp = app(mkBinOp, symbol('+') | symbol('-') | symbol('*') | symbol('/')); Applicative is combinable 35
  35. 35. ParserT<Value> value = app(mkValue, intP | doubleP); ParserT<BinOp> binOp = app(mkBinOp, symbol('+') | symbol('-') | symbol('*') | symbol('/')); ParserT<FuncArgs> funcArgs = app(mkFuncArgs, expr, opt(symbol(',') >> funcArgs)); Applicative is combinable 36
  36. 36. ParserT<Value> value = app(mkValue, intP | doubleP); ParserT<BinOp> binOp = app(mkBinOp, symbol('+') | symbol('-') | symbol('*') | symbol('/')); ParserT<FuncArgs> funcArgs = app(mkFuncArgs, expr, opt(symbol(',') >> funcArgs)); ParserT<FuncCall> funcCall = app(mkFuncCall, identifier, between('(', ')', funcArgs)); Applicative is combinable 37
  37. 37. ParserT<Value> value = app(mkValue, intP | doubleP); ParserT<BinOp> binOp = app(mkBinOp, symbol('+') | symbol('-') | symbol('*') | symbol('/')); ParserT<FuncArgs> funcArgs = app(mkFuncArgs, expr, opt(symbol(',') >> funcArgs)); ParserT<FuncCall> funcCall = app(mkFuncCall, identifier, between('(', ')', funcArgs)); ParserT<BinExpr> binExpr = app(mkBinExpr, expr, binOp, expr); Applicative is combinable 38
  38. 38. ParserT<Value> value = app(mkValue, intP | doubleP); ParserT<BinOp> binOp = app(mkBinOp, symbol('+') | symbol('-') | symbol('*') | symbol('/')); ParserT<FuncArgs> funcArgs = app(mkFuncArgs, expr, opt(symbol(',') >> funcArgs)); ParserT<FuncCall> funcCall = app(mkFuncCall, identifier, between('(', ')', funcArgs)); ParserT<BinExpr> binExpr = app(mkBinExpr, expr, binOp, expr); ParserT<Expr> expr = app(mkExpr, binExpr | funcCall | value); Applicative is combinable 39
  39. 39. Functor fmap :: (A -> B) -> ParserT<A> -> ParserT<B> Applicative app :: (A -> B -> C) -> ParserT<A> -> ParserT<B> -> ParserT<B> Monad bind :: ParserT<A> -> (A -> ParserT<B>) 40
  40. 40. template <typename A> ParserT<A> pure(const A& a); template <typename A, typename B> ParserT<B> bind( const ParserT<A>& ma, const function<ParserT<B>(A)>& f); ParserT monad 41
  41. 41. ParserT<string> helloUsername = bind<string, string>(letters, [=](auto firstName) { return bind<string, string>(spaces, [=](auto) { return bind<string, string>(letters, [=](auto lastName) { return pure<string>(mkHello(firstName, lastName)); }); }); }); ParserT<string> helloUsername = bind<string, string>(letters, [=](auto firstName) { return bind<string, string>(spaces, [=](auto) { return bind<string, string>(letters, [=](auto lastName) { return pure<string>(mkHello(firstName, lastName)); }); }); }); Monadic binding 42
  42. 42. ParserT<string> helloUsername = bind<string, string>(letters, [=](auto firstName) { return bind<string, string>(spaces, [=](auto) { return bind<string, string>(letters, [=](auto lastName) { return pure<string>(mkHello(firstName, lastName)); }); }); }); ParserT<string> helloUsername = bind<string, string>(letters, [=](auto firstName) { return bind<string, string>(spaces, [=](auto) { return bind<string, string>(letters, [=](auto lastName) { return pure<string>(mkHello(firstName, lastName)); }); }); }); Monadic binding, reformatted 43
  43. 43. ParserT<string> helloUsername = do { auto firstName <- letters; auto _ <- spaces; auto lastName <- letters; return pure<string>(mkHello(firstName, lastName)); }; Dear Santa, I was a good boy last year. Can I have?.. 44
  44. 44. State & context-sensitive grammars 45
  45. 45. age integer first name string last name string salary float employee {35, “Jane”, “Street”, 54321} address {35, “Jane”, “Street”, 54321} house integer title string post index integer type string 46
  46. 46. struct Employee { int age; string firstName; string lastName; double salary; }; 47 struct Address { int house; string title; string name; int postIndex; }; Algebraic Data Type
  47. 47. struct Employee { int age; string firstName; string lastName; double salary; }; 48 struct Address { int house; string title; string name; int postIndex; }; using LogEntry = std::variant<Employee, Address>; Algebraic Data Type
  48. 48. struct Employee { int age; string firstName; string lastName; double salary; }; 49 struct Address { int house; string title; string name; int postIndex; }; using LogEntry = std::variant<Employee, Address>; function<LogEntry(int a, const string& fn, const string& ln, double s)> mkEmployeeEntry; function<LogEntry(int h, const string& ttl, const string& n, int idx)> mkAddressEntry; Algebraic Data Type
  49. 49. struct Config { string currentEntry; }; ParserT<Config, string> logToken = lit("employee") | lit("address"); ParserT<Config, Unit> setEntry = bind<string, Unit>( logToken, [](const string& s) { return putSt(Config { s }); }); Putting state 50
  50. 50. struct Config { string currentEntry; }; ParserT<Config, string> logToken = lit("employee") | lit("address"); ParserT<Config, Unit> setEntry = bind<string, Unit>( logToken, [](const string& s) { return putSt(Config { s }); }); Putting state 51
  51. 51. ParserT<Config, LogEntry> logEntry = bind<Config, LogEntry>(getSt, [=](Config cfg) { if (cfg.currentEntry == "employee") return fmap(mkEmployeeEntry, employeeParser); return fmap(mkAddressEntry, addressParser); }); Getting state 52
  52. 52. ParserT<Config, LogEntry> logEntry = bind<Config, LogEntry>(getSt, [=](Config cfg) { if (cfg.currentEntry == "employee") return fmap(mkEmployeeEntry, employeeParser); return fmap(mkAddressEntry, addressParser); }); Getting state 53
  53. 53. Conclusion 54
  54. 54. Monadic parser combinators ● Monad: do-notation, bind, when, unless, sequence, mapM, guard ● Applicative: app, pure ● Alternative: alt, operator| ● Functor: fmap ● Time travelling: lookForward, lookBackward ● State handling: getSt, putSt ● Error handling: tryP, safeP, onFail, onSuccess, failWith 55
  55. 55. 56 GitHub List: Functional Programming in C++ https://github.com/graninas/cpp_functional_programming�� ● Books ● Articles ● Papers ● Talks ● QA ● Libraries ● Showcase projects
  56. 56. Thanks for watching! Alexander Granin graninas@gmail.com Twitter: @graninas C++ Russia 2019, Moscow 57

×