5/10/2015 1 Domain-Specific Embedded Languages with Boost.Proto Or, "How I Learned to Stop Worrying and Love Expression Templates"

Slides:



Advertisements
Similar presentations
Boost Writing better code faster with Boost. Boost Introduction Collection of C++ libraries Boost includes 52 libraries, 1.31 will have at least.
Advertisements

The C ++ Language BY Shery khan. The C++ Language Bjarne Stroupstrup, the language’s creator C++ was designed to provide Simula’s facilities for program.
Introduction to Programming in C++ John Galletly.
Boost Spirit V2 A cookbook style guide to parsing and output generation in C++ Joel de Guzman Hartmut Kaiser
Abstract Syntax Mooly Sagiv html:// 1.
1 JavaCUP JavaCUP (Construct Useful Parser) is a parser generator Produce a parser written in java, itself is also written in Java; There are many parser.
Domain Specific Embedded Languages Lecture 2, Designing and Using Combinators John Hughes.
Chapter 7Louden, Programming Languages1 Chapter 7 - Control I: Expressions and Statements "Control" is the general study of the semantics of execution.
Computer Science 1620 Loops.
Functional Design and Programming Lecture 9: Lexical analysis and parsing.
. Templates. Example… A useful routine to have is void Swap( int& a, int &b ) { int tmp = a; a = b; b = tmp; }
Object-Oriented Programming MISM/MSIT Carnegie Mellon University Professor Roehrig’s Notes.
Templates. Objectives At the conclusion of this lesson, students should be able to Explain how function templates are used Correctly create a function.
Extending Type Systems in a Library Yuriy Solodkyy Jaakko Järvi Esam Mlaih.
Chapter 7Louden, Programming Languages1 Chapter 7 - Control I: Expressions and Statements "Control" is the general study of the semantics of execution.
C++ Programming Language Day 1. What this course covers Day 1 – Structure of C++ program – Basic data types – Standard input, output streams – Selection.
Introduction to C++ Programming
C++ Functions. 2 Agenda What is a function? What is a function? Types of C++ functions: Types of C++ functions: Standard functions Standard functions.
CSC 8310 Programming Languages Meeting 2 September 2/3, 2014.
CSE 332: C++ Algorithms II From Last Time: Search with Generic Iterators Third generalization: separate iterator type parameter We arrive at the find algorithm.
9/10/ Text Processing With Boost Or, "Anything you can do, I can do better"
COMS W4115 Programming Languages & Translators Maria Ayako Taku COMS W PLT Columbia University 1 April 24, 2013 Functional Programming.
 2003 Prentice Hall, Inc. All rights reserved. 1 Functions and Recursion Outline Function Templates Recursion Example Using Recursion: The Fibonacci Series.
Outlines Chapter 3 –Chapter 3 – Loops & Revision –Loops while do … while – revision 1.
Design Pattern Interpreter By Swathi Polusani. What is an Interpreter? The Interpreter pattern describes how to define a grammar for simple languages,
C++ Template Metaprogramming Why, When and How? Zoltán Porkoláb Dept. of Programming Languages and Compilers, Faculty of Informatics Eötvös.
Lesson 3 CDT301 – Compiler Theory, Spring 2011 Teacher: Linus Källberg.
CSC1201: Programming Language 2 Lecture 1 Level 2 Course Nouf Aljaffan Snd Term Nouf Aljaffan (C) CSC 1201 Course at KSU1.
Copyright 2005 Eric Niebler xpressive: Library Design on the Edge or, How I Learned to Stop Worrying and Love Template Meta- Programming.
3. The Nuts and Bolts of C++ Computer Programming 3. The Nuts and Bolts of C++ 1 Learning the C++ language 3. The Nuts and Bolts of C++
Interpretation Environments and Evaluation. CS 354 Spring Translation Stages Lexical analysis (scanning) Parsing –Recognizing –Building parse tree.
Copyright  Hannu Laine C++-programming Part 1 Hannu Laine.
GPCE'04, Vancouver 1 Towards a General Template Introspection Library in C++ István Zólyomi, Zoltán Porkoláb Department of Programming Languages and Compilers.
(c) 2004, Eric Niebler FOR_EACH by Eric Niebler or, How I Learned to Stop Worrying and Love the Preprocessor.
C++ Basics C++ is a high-level, general purpose, object-oriented programming language.
Looping and Counting Lecture 3 Hartmut Kaiser
CPS 506 Comparative Programming Languages Syntax Specification.
Control Structures (B) Topics to cover here: Sequencing in C++ language.
1 Lecture 14 Functions Functions with Empty Parameter Lists Empty parameter lists  void or leave parameter list empty  Indicates function takes.
1 LEX & YACC Tutorial February 28, 2008 Tom St. John.
CSC 143A 1 CSC 143 Introduction to C++ [Appendix A]
CS1201: PROGRAMMING LANGUAGE 2 FUNCTIONS. OVERVIEW What is a Function? Function Prototype Vs Decleration Highlight Some Errors in Function Code Parameters.
C++ Programming Lecture 13 Functions – Part V The Hashemite University Computer Engineering Department (Adapted from the textbook slides)
1 Advanced Topics in Functions Lecture Unitary Scope Resolution Operator Unary scope resolution operator ( :: )  Access global variable if.
 2003 Prentice Hall, Inc. All rights reserved. 1 Chapter 3 - Functions Outline 3.15Functions with Empty Parameter Lists 3.16Inline Functions 3.17References.
CSC1201: Programming Language 2 1 Functions. 2 Function declaration: return_type FuncName( Type arg1, Type arg2,….. Type argN) { function body } A program.
2/26/ Customization Points and Polymorphism Functional Programming Boost.Lambda.
FUNCTIONAL PROGRAMING AT WORK - HASKELL AND DOMAIN SPECIFIC LANGUAGES Dr. John Peterson Western State Colorado University.
CSIS 113A Lecture 5 Functions. Introduction to Functions  Building Blocks of Programs  Other terminology in other languages:  Procedures, subprograms,
Fundamental Programming Fundamental Programming Data Processing and Expressions.
C++ Programming Lecture 13 Functions – Part V By Ghada Al-Mashaqbeh The Hashemite University Computer Engineering Department.
Chapter 3 – Describing Syntax
Chapter 1.2 Introduction to C++ Programming
COMP261 Lecture 18 Parsing 3 of 4.
Chapter 2 Introduction to C++ Programming
CMPE 152: Compiler Design April 5 Class Meeting
CSC1201: Programming Language 2
CSE 3302 Programming Languages
Parsing & Scanning Lecture 2
Lecture 7: Introduction to Parsing (Syntax Analysis)
slides created by Marty Stepp
Adapted from slides by Nicholas Shahan and Dan Grossman
Adapted from slides by Nicholas Shahan, Dan Grossman, and Tam Dang
Jordi Cortadella and Jordi Petit Department of Computer Science
The Recursive Descent Algorithm
CSC1201: Programming Language 2
Functions Imran Rashid CTO at ManiWeber Technologies.
Chapter 1 c++ structure C++ Input / Output
Presentation transcript:

5/10/ Domain-Specific Embedded Languages with Boost.Proto Or, "How I Learned to Stop Worrying and Love Expression Templates"

copyright 2007 Eric Niebler 5/10/ Talk Overview Goal: Designing declarative domain-specific libraries using Boost.Proto. 1. Domain Specific Embedded Languages 2. DSEL Design with Boost.Proto 1. Expression construction 2. Expression evaluation 3. Expression introspection 4. Expression transformation 3. Extending Boost.Proto

copyright 2007 Eric Niebler 5/10/ Hello, Boost.Proto! // #includes... using namespace boost::proto; terminal ::type cout_ = { std::cout }; template void evaluate( Expr const & expr ) { default_context ctx; eval(expr, ctx); } int main() { evaluate( cout_ << "hello" << ',' << " world" ); }

copyright 2007 Eric Niebler 5/10/ Hello, Boost.Proto! // #includes... using namespace boost::proto; terminal ::type cout_ = { std::cout }; int main() { display_expr( cout_ << "hello" << ',' << " world\n\n" ); return 0; }

5/10/ Domain-Specific Embedded Languages Or, "Operator Abuse for Fun and Profit!"

copyright 2007 Eric Niebler 5/10/ DSEL Example: Boost.Lambda DSEL for creating unnamed function objects (and very long compiler errors) Jaakko Järvi std::for_each( a.begin(), a.end(), std::cout << _1 << ' ' );

copyright 2007 Eric Niebler 5/10/ DSEL Example: Boost.Spirit Parser Generator  similar in purpose to lex / YACC DSEL for declaring grammars  grammars can be recursive  Approximates Backus-Naur Form Joel de Guzman

copyright 2007 Eric Niebler 5/10/ Infix Calculator Grammar In Extended Backus-Naur Form group ::= '(' expr ')' fact ::= integer | group term ::= fact (('*' fact) | ('/' fact))* expr ::= term (('+' term) | ('-' term))*

copyright 2007 Eric Niebler 5/10/ template struct Op {... }; template struct BinOp : Op > { L const & left; R const & right;... }; template BinOp const operator >> ( Op const & l, Op const & r ) {... } What is an Expression Template? Tree representation of an expression Built using templates and operator overloading Builds the tree representation of " expr1 >> expr2 ". BinOp<> is a container for two sub-expressions.

copyright 2007 Eric Niebler 5/10/ group ::= '(' expr ')' fact ::= integer | group term ::= fact (('*' fact) | ('/' fact))* expr ::= term (('+' term) | ('-' term))* Infix Calculator Grammar In Extended Backus-Naur Form In Boost.Spirit spirit::rule group, fact, term, expr; group = '(' >> expr >> ')'; fact = spirit::int_p | group; term = fact >> *(('*' >> fact) | ('/' >> fact)); expr = term >> *(('+' >> term) | ('-' >> term));

copyright 2007 Eric Niebler 5/10/ rule group = '(' >> expr >> ')'; What is an Expression Template? DSELs like Spirit interpret expression trees in a domain-specific way. >> ')' '(' expr BinOp< RShift, BinOp< RShift, Term, Term >, Term >

copyright 2007 Eric Niebler 5/10/ rule group = '(' >> expr >> ')' What is an Expression Template? DSELs like Spirit interpret expression trees in a domain-specific way. struct rule { template rule & operator= ( Op const & op ) {... magic happens here... return *this; }... }; Somehow interpret op as a grammar rule. This is hard!

copyright 2007 Eric Niebler 5/10/ DSELs in C++, the Sad Truth Very difficult to design Code is hard to read and maintain Terrible compiler error messages So why not a DSEL for defining DSELs?!  Expression construction  Expression evaluation  Expression introspection  Expression transformation

5/10/ DSEL Design with Boost.Proto Or, “The Importance of Being Grammatically Correct"

copyright 2007 Eric Niebler 5/10/ A Calculator DSEL template struct placeholder : Int {}; terminal > >::type _1; terminal > >::type _2; int main() { (_1 + _2); return 0; } Expression construction expr > >, 0 > &, expr< tag::terminal, term > >, 0 > & >, 2>

copyright 2007 Eric Niebler 5/10/ Calculator, Reloaded // To be defined... struct calculator_context //... int main() { // Placeholders have values 1. and 2. respectively: calculator_context ctx(1., 2.); // Evaluate a calculator expression with the // calculator_context std::cout << eval(_1 + _2, ctx) << std::endl; } Expression evaluation

copyright 2007 Eric Niebler 5/10/ Calculator, Reloaded // This describes how to evaluate calculator expressions struct calculator_context : callable_context { double d[2]; typedef double result_type; calculator_context(double d1, double d2) { d[0] = d1; d[1] = d2; } template double operator()(tag::terminal, placeholder i) const { return d[i-1]; // handle argument placeholders } }; Expression evaluation, continued

copyright 2007 Eric Niebler 5/10/ The Context Concept All context types must look like this: struct Context { template struct eval { typedef unspecified result_type; result_type operator()(Expr &expr, Context &context) const; }; Helpers for building contexts  default_context / default_eval  callable_context / callable_eval

copyright 2007 Eric Niebler 5/10/ default_context, detail. struct default_context { template struct eval : default_eval {}; }; template //, class T = typename Expr::tag_type struct default_eval { typedef ?????? result_type; result_type operator()(Expr &expr, Ctx &ctx) const { return eval(left(expr),ctx) + eval(right(expr),ctx); } }; struct default_context { template struct eval : default_eval {}; }; template //, class T = typename Expr::tag_type struct default_eval { static Expr &sexpr; static Ctx &sctx; typedef BOOST_TYPEOF(eval(left(sexpr),sctx) + eval(right(sexpr),sctx)) result_type; result_type operator()(Expr &expr, Ctx &ctx) const { return eval(left(expr),ctx) + eval(right(expr),ctx); } };

copyright 2007 Eric Niebler 5/10/ callable_context, detail. template struct callable_context { template struct eval : callable_eval {}; }; template //, int I = Expr::proto_arity struct callable_eval { typedef typename Derived::result_type result_type; result_type operator()(Expr &expr, Derived &ctx) const { return ctx(typename Expr::tag_type(), arg_c (expr), arg_c (expr)); } };

copyright 2007 Eric Niebler 5/10/ Expression Introspection Query the properties of expression trees at compile time Utilities for defining grammars proto::matches<> for grammar- checking your expressions. “Be grammatical!"

copyright 2007 Eric Niebler 5/10/ An Input/Output DSEL // Terminals for lazy input/output expressions: terminal ::type cin_ = { std::cin }; terminal ::type cout_ = { std::cout }; template void do_io( IO const & io ) { eval( io, default_context() ); } int main() { int i = 0; do_io( cin_ >> i ); // reads an int do_io( cout_ << i ); // writes an int } What if do_io() needs to know whether IO represents an input or an output operation?

copyright 2007 Eric Niebler 5/10/ Answer: Input/Output Patterns // A pattern to match Input expressions struct Input : shift_right, _ > {}; // A pattern to match Output expressions struct Output : shift_left, _ > {}; template void do_io(IO const &io) { if( matches ::value ) std::cout << "Input!\n"; if( matches ::value ) std::cout << "Output!\n"; //... } Evaluated at compile time! Matches " cin_ >> i " Matches " cout_ << i "

copyright 2007 Eric Niebler 5/10/ Whoops! A Problem... // A pattern to match Output expressions struct Output : shift_left, _ > {}; Why doesn't this expression match the Output pattern? cout_ << 1 << 2; shift_left< terminal ::type, terminal ::type >::type, terminal ::type >::type ( cout_ << 1 ) << 2;

copyright 2007 Eric Niebler 5/10/ Solution: A Recursive Pattern // A pattern to match Output expressions struct Output : or_< shift_left, _ >, shift_left > {}; or_<> alternates tried in order Short-circuit evaluation Notice recursion on Output pattern! // A pattern to match Output expressions struct Output : or_< shift_left, _ >, shift_left > {};

copyright 2007 Eric Niebler 5/10/ From Patterns to Grammars struct Calculator; struct Double : terminal {}; struct Placeholder : terminal > {}; struct Plus : plus {}; struct Minus : minus {}; struct Multiplies : multiplies {}; struct Divides : divides {}; struct Calculator : or_< Double, Placeholder, Plus, Minus, Multiplies, Divides > {}; A Proto Grammar for our Calculator:

copyright 2007 Eric Niebler 5/10/ Proto vs. EBNF Are Proto grammars as powerful as EBNF? group ::= '(' expr ')' fact ::= double | group term ::= fact (('*' fact) | ('/' fact))* expr ::= term (('+' term) | ('-' term))* struct Expr; struct Dbl : terminal {}; struct Plus : plus {}; struct Minus : minus {}; struct Mult : multiplies {}; struct Div : divides {}; struct Expr : or_ {}; EBNFProto Why don’t Proto grammars need: Sequencing? Repetition? Precedence? Associativity?

copyright 2007 Eric Niebler 5/10/ What are grammars good for? // A function that only accepts valid calculator expressions! template typename enable_if >::type evaluate( Expr const & expr ) { //... } enable_if<> tricks to prune overload sets mpl::if_<> for type selections Compile-time assertions Defining tree transformations Controlling Proto's operator overloading "It's the Grammar, stupid."

copyright 2007 Eric Niebler 5/10/ Expression Transformation Turns an expression tree into some other object Transformations attached to grammar rules Transformations provided by Proto or user defined Challenge! For any calculator expression, find the arity. Eg.: _ has arity 1 _2 * 42 has arity 2

copyright 2007 Eric Niebler 5/10/ Arity Calculation ExpressionArity double0 _11 _22 Left op Rightmax(arity(Left), arity(Right)) Calculator expression arity is calculated as follows:

copyright 2007 Eric Niebler 5/10/ From Grammars to Transforms // Ye olde calculator grammar struct Arity; struct Double : terminal {}; struct Placeholder : terminal > {}; struct Plus : plus {}; struct Minus : minus {}; struct Multiplies : multiplies {}; struct Divides : divides {}; struct Arity : or_< Double, Placeholder, Plus, Minus, Multiplies, Divides > {};

copyright 2007 Eric Niebler 5/10/ // Ye olde calculator grammar struct Arity; struct Double : terminal {}; struct Placeholder : terminal > {}; struct Plus : plus {}; struct Minus : minus {}; struct Multiplies : multiplies {}; struct Divides : divides {}; struct Arity : or_< Double, Placeholder, Plus, Minus, Multiplies, Divides > {}; Double Transform struct Double : terminal {}; struct Arity : or_< case_ () >,... > {}; Anything that matches Double is transformed into mpl::int_

copyright 2007 Eric Niebler 5/10/ Placeholder Transform template struct placeholder : Int {}; terminal > >::type _1; struct Placeholder : terminal > {}; struct Arity : or_<..., case_,... > {}; Proto’s _arg transform pulls the placeholder<> argument out of the terminal<>. And a placeholder<> IS-A compile-time integer representing its arity.

copyright 2007 Eric Niebler 5/10/ Binary Operator Transform // A Lambda for evaluating the max arity of a binary // calculator expression. typedef mpl::max MaxArity; struct Arity : or_<..., case_, MaxArity() >,... > {}; Anything that matches plus is transformed according to the MaxArity() transform.

copyright 2007 Eric Niebler 5/10/ Fun with C++ Declaration Syntax What are all the things that this can mean? X( Y ) Function call Constructor call Function type Function-style cast

copyright 2007 Eric Niebler 5/10/ mpl::max () Lambda Transforms Step 1: Recursively evaluate nested lambdas mpl::max () mpl::max, mpl::int_ >() Evaluate left and right sub- expressions according to the Arity transform.

copyright 2007 Eric Niebler 5/10/ mpl::max, mpl::int >() mpl::max, mp::int >::type() mpl::int_ () Lambda Transforms Step 2: Evaluate meta-function ` Look for a nested ::type typedef. `

copyright 2007 Eric Niebler 5/10/ mpl::int_ () Lambda Transforms Step 3: Call object constructor The function type is interpreted as a constructor invocation. Proto returns a default constructed mpl::int_ !

copyright 2007 Eric Niebler 5/10/ The Complete Transform struct Arity; struct Double : terminal {}; struct Placeholder : terminal > {}; struct Plus : plus {}; struct Minus : minus {}; struct Multiplies : multiplies {}; struct Divides : divides {}; typedef mpl::max MaxArity; struct Arity : or_ () >, case_ > {};

copyright 2007 Eric Niebler 5/10/ It works! // Defined as before struct Arity... ; // Display the arity of a calculator expression template void print_arity(Expr const & expr) { BOOST_MPL_ASSERT((matches )); int i = 0; // dummy, not used std::cout << Arity::call(expr, i, i) << std::endl; } int main() { print_arity(_1 + 42); print_arity(_2 * 42); }

5/10/ Extending Proto Giving Proto expressions extra smarts.

copyright 2007 Eric Niebler 5/10/ Calculator, Reloaded // To be defined... struct calculator_context //... int main() { // Placeholders have values 1. and 2. respectively: calculator_context ctx(1., 2.); // Evaluate a calculator expression with the calculator_context std::cout << eval(_1 + _2, ctx) << std::endl; } Expression evaluation flashback Q: Is eval() always needed to make proto expressions do something?

copyright 2007 Eric Niebler 5/10/ Calculator, Reloaded int main() { double data[] = { 1., 2., 3., 4. }; // Use the calculator DSEL to square each element std::transform( data, data + 4, data, _1 * _1 ); } A Calculator expression as an STL functor? Whoops! " _1 * _1 " doesn't have an operator() that takes and returns a double.

copyright 2007 Eric Niebler 5/10/ Calculator, Revolutions struct calc_domain; template struct calc : extends, calc_domain > { calc( Expr const &expr = Expr() ) : extends, calc_domain >( expr ) {} typedef double result_type; result_type operator()( double d1 = 0.0, double d2 = 0.0 ) const { calculator_context ctx( d1, d2 ); return eval( *this, ctx ); } }; Expression wrappers to add behaviors The "domain" of our wrapped expressions calc extends Expr Define operator() to evaluate expr with calculator_context

copyright 2007 Eric Niebler 5/10/ Calculator, Revolutions // OK, _1 and _2 are calc<> expressions in the calc_domain calc > >::type > _1; calc > >::type > _2; Wrap our terminals... struct calc_domain : domain > {}; Hook Proto's expression generator... In calc_domain, wrap all expression types in our calc<> wrapper.

copyright 2007 Eric Niebler 5/10/ Calculator, Revolutions It works!

copyright 2007 Eric Niebler 5/10/ Summing up... Proto makes C++ DSEL design easy and fun! Good for expression template... ... construction ... evaluation ... introspection ... transformation Powerful extensibility mechanisms Used by xpressive, Spirit-2, Karma, etc... Available in Boost Subversion and the File Vault

5/10/ Questions?