Domain Specific Embedded Languages Lecture 2, Designing and Using Combinators John Hughes.

Slides:



Advertisements
Similar presentations
Abstract Syntax Mooly Sagiv html:// 1.
Advertisements

Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 2 Syntax A language that is simple to parse.
1 Pass Compiler 1. 1.Introduction 1.1 Types of compilers 2.Stages of 1 Pass Compiler 2.1 Lexical analysis 2.2. syntactical analyzer 2.3. Code generation.
What is a Parser? A parser is a program that analyses a piece of text to determine its syntactic structure  3 means 23+4.
CSE 3302 Programming Languages Chengkai Li, Weimin He Spring 2008 Syntax Lecture 2 - Syntax, Spring CSE3302 Programming Languages, UT-Arlington ©Chengkai.
9/27/2006Prof. Hilfinger, Lecture 141 Syntax-Directed Translation Lecture 14 (adapted from slides by R. Bodik)
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 2 Syntax A language that is simple to parse.
CS 330 Programming Languages 09 / 13 / 2007 Instructor: Michael Eckmann.
Context-Free Grammars Lecture 7
Introducing Monads Lecture 3, Designing and Using Combinators John Hughes.
Slide 1 Chapter 2-b Syntax, Semantics. Slide 2 Syntax, Semantics - Definition The syntax of a programming language is the form of its expressions, statements.
Two Case Studies: QuickCheck and Wash/CGI Lecture 4, Designing and Using Combinators John Hughes.
Chapter 3 Program translation1 Chapt. 3 Language Translation Syntax and Semantics Translation phases Formal translation models.
Yu-Chen Kuo1 Chapter 2 A Simple One-Pass Compiler.
Winter 2003/4Pls – syntax – Catriel Beeri1 SYNTAX Syntax: form, structure The syntax of a pl: The set of its well-formed programs The rules that define.
CSC 8310 Programming Languages Meeting 2 September 2/3, 2014.
Abstract Syntax Trees Lecture 14 Wed, Mar 3, 2004.
1 Introduction to Parsing Lecture 5. 2 Outline Regular languages revisited Parser overview Context-free grammars (CFG’s) Derivations.
Computer Science 112 Fundamentals of Programming II Expression Trees.
Prof. Bodik CS 164 Lecture 51 Building a Parser I CS164 3:30-5:00 TT 10 Evans.
Chapter 1 Introduction Dr. Frank Lee. 1.1 Why Study Compiler? To write more efficient code in a high-level language To provide solid foundation in parsing.
Chapter 10: Compilers and Language Translation Invitation to Computer Science, Java Version, Third Edition.
CS 461 – Oct. 7 Applications of CFLs: Compiling Scanning vs. parsing Expression grammars –Associativity –Precedence Programming language (handout)
COMP Parsing 2 of 4 Lecture 22. How do we write programs to do this? The process of getting from the input string to the parse tree consists of.
Deriving Combinator Implementations Lecture 4, Designing and Using Combinators John Hughes.
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 2 Syntax A language that is simple to parse.
Topic #2: Infix to Postfix EE 456 – Compiling Techniques Prof. Carl Sable Fall 2003.
1 Compiler Construction (CS-636) Muhammad Bilal Bashir UIIT, Rawalpindi.
Interpretation Environments and Evaluation. CS 354 Spring Translation Stages Lexical analysis (scanning) Parsing –Recognizing –Building parse tree.
Lexical and Syntax Analysis
Introduction to Parsing
CPS 506 Comparative Programming Languages Syntax Specification.
CS412/413 Introduction to Compilers and Translators Spring ’99 Lecture 3: Introduction to Syntactic Analysis.
Simple Language (SL) Start version 00. Atze Dijkstra, 2000IPT - SL2 Content SL, the language AG, attribute grammar system First version of SL compiler.
Unit-3 Parsing Theory (Syntax Analyzer) PREPARED BY: PROF. HARISH I RATHOD COMPUTER ENGINEERING DEPARTMENT GUJARAT POWER ENGINEERING & RESEARCH INSTITUTE.
1 A Simple Syntax-Directed Translator CS308 Compiler Theory.
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 2 Syntax A language that is simple to parse.
C H A P T E R T W O Linking Syntax And Semantics Programming Languages – Principles and Paradigms by Allen Tucker, Robert Noonan.
Overview of Previous Lesson(s) Over View 3 Model of a Compiler Front End.
Chap. 7, Syntax-Directed Compilation J. H. Wang Nov. 24, 2015.
Parser: CFG, BNF Backus-Naur Form is notational variant of Context Free Grammar. Invented to specify syntax of ALGOL in late 1950’s Uses ::= to indicate.
FUNCTIONAL PROGRAMING AT WORK - HASKELL AND DOMAIN SPECIFIC LANGUAGES Dr. John Peterson Western State Colorado University.
CS 404Ahmed Ezzat 1 CS 404 Introduction to Compiler Design Lecture 1 Ahmed Ezzat.
Syntax Analysis Or Parsing. A.K.A. Syntax Analysis –Recognize sentences in a language. –Discover the structure of a document/program. –Construct (implicitly.
CS 404Ahmed Ezzat 1 CS 404 Introduction to Compiler Design Lecture Ahmed Ezzat.
Chapter 3 – Describing Syntax CSCE 343. Syntax vs. Semantics Syntax: The form or structure of the expressions, statements, and program units. Semantics:
What is a Parser? A parser is a program that analyses a piece of text to determine its syntactic structure  3 means 23+4.
Chapter 3 – Describing Syntax
Lexical and Syntax Analysis
Introduction to Parsing
CS510 Compiler Lecture 4.
Chapter 3 Context-Free Grammar and Parsing
PROGRAMMING IN HASKELL
Chapter 3 – Describing Syntax
Abstract Syntax Trees Lecture 14 Mon, Feb 28, 2005.
PROGRAMMING LANGUAGES
-by Nisarg Vasavada (Compiled*)
Lexical and Syntax Analysis
Parsing & Scanning Lecture 2
PROGRAMMING IN HASKELL
Chapter 2: A Simple One Pass Compiler
R.Rajkumar Asst.Professor CSE
CS 3304 Comparative Languages
CS 3304 Comparative Languages
Programming Languages 2nd edition Tucker and Noonan
PROGRAMMING IN HASKELL
Chapter 10: Compilers and Language Translation
PROGRAMMING IN HASKELL
Programming Languages 2nd edition Tucker and Noonan
Faculty of Computer Science and Information System
Presentation transcript:

Domain Specific Embedded Languages Lecture 2, Designing and Using Combinators John Hughes

What is a Domain Specific Language? A programming language tailored for a particular application domain, which captures precisely the semantics of the application domain -- no more, no less. A DSL allows one to develop software for a particular application domain quickly, and effectively, yielding programs that are easy to understand, reason about, and maintain. Hudak

The Cost Argument Software life cycle Total SW cost Conventional methodology DSL-based methodology Start up cost

The Problem with DSLs DSLs tend to grow: adding procedures, modules, data structures… Language design is difficult and time-consuming; large parts are not domain specific. Implementing a compiler is costly (code-generation, optimisation, type-checking, error messages…) Start up costs may be substantial!

Domain Specific Embedded Languages Why not embed the DSL as a library in an existing host language? Inherit non-domain-specific parts of the design. Inherit compilers and tools. Uniform “look and feel” across many DSLs DSLs integrated with full programming language, and with each other. + Constrained by host language syntax and type system. -

The Cost Argument Again Software life cycle Total SW cost Conventional methodology DSL-based methodology Start up cost DSEL-based methodology Much lower start-up cost

What Makes Haskell a Suitable Host? Higher-order functions. DSL library constructs programs, naturally represented as functions. Lazy evaluation. Permits recursive definitions of DSL programs, “if-then- else” as a function. Polymorphism and class system. Powerful type system to embed DSL’s types in. Classes permit definitions by “recursion over types”.

Example: DSL vs DSEL for Parsing YACC is a well-known DSL for parsers. HAPPY is a YACC-like tool for Haskell. Parsing combinators are a classic DSEL for Haskell. Compare approaches in an evaluator for arithmetic expressions.

Expression BNF expr ::= term | term + term term ::= factor | factor * factor factor ::= constant | (expr) constant ::= digit* Examples 1+2, 1+2*3, (1+2)*3, but not 1+2+3

Lexical Analyser Happy requires an external lexical analyser: data Token = TokenInt Int | TokenPlus | TokenTimes | TokenBra | TokenKet deriving (Show,Eq) lexer :: String -> [Token]

Happy Grammar: Tokens %name calc %tokentype { Token } %token int {TokenInt $$} '+' {TokenPlus} '*' {TokenTimes} '(' {TokenBra} ')' {TokenKet} % Name of function to generate Relate token names to Haskell values

Happy Grammar: Syntax Exp : Term '+' Term {$1+$3} | Term {$1} Term : Factor '*' Factor {$1*$3} | Factor {$1} Factor: int {$1} | '(' Exp ')' {$2} Actions to evaluate

End Result Calc> calc (lexer “(1+2)*3”) 9 Calc>

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr Sequencing using do

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr Results named in the usual way Sequencing using do

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr Results named in the usual way Sequencing using do Literal tokens (cf %token section)

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr Results named in the usual way Sequencing using do Literal tokens (cf %token section) “Action”

A Combinator Parser expr = do t <- term literal TokenPlus t' <- term return (t+t') ||| term term = do f <- factor literal TokenTimes f' <- factor return (f*f') ||| factor factor = do literal TokenBra e <- expr literal TokenKet return e ||| do TokenInt n <- satisfy isTokenInt return n calc = runParser expr Results named in the usual way Sequencing using do Literal tokens (cf %token section) “Action” Alternatives

Abstracting Common Patterns The combinator parser is not quite as concise as the DSL one - - we cannot invent new syntax! But we can exploit the power of Haskell to abstract common patterns! Example: Binary operators binOp oper rand = do x <- rand op <- oper y <- rand return (x `op` y) ||| rand expr = binOp (do literal TokenPlus return (+)) term

Further Abstraction expr = binOp (do literal TokenPlus return (+)) term Appears for every operator

Further Abstraction expr = binOp (literal TokenPlus `giving` (+)) term

Further Abstraction expr = binOp (literal TokenPlus `giving` (+)) term term = binOp (literal TokenTimes `giving` (*)) factor

Extending the Operators expr = binOp (literal TokenPlus `giving` (+) ||| literal TokenMinus `giving` (-)) term term = binOp (literal TokenTimes `giving` (*) ||| literal TokenDivide `giving` div) factor Thanks to the embedding in Haskell, we can “extend the language” to obtain concise parsers in each particular case.

Extending the Grammar What about accepting 1+2+3? Operators associate to the left: = (1-2)-3 Easy with Happy! Exp : Exp '+' Term {$1+$3} | Term {$1} Term : Term '*' Factor {$1*$3} | Factor {$1} Factor: int {$1} | '(' Exp ')' {$2}

Extending the Combinator Parser? expr = do t <- expr literal TokenPlus t' <- term return (t+t') ||| term Left recursion makes the parser loop! Happy works because it compiles the left recursion away.

Extending the Combinator Parser: The Right Way Transform the grammar: parse as right recursion, convert the result to associate left! E ::= E Op T | TE ::= T {Op T}* binOp oper rand = do x <- rand op_ys <- many (oper `pair` rand) return (foldl (\z (op,y) -> z `op` y) x op_ys) p `pair` q = do x <- p y <- q return (x,y) expr and term need not change at all!

What is the DSEL? p ::= do {x <- p}* p | return e | literal tok | satisfy pred | p ||| p | many p | p `pair` p | binOp p p | p `giving` e It is very easy to extend the language further!

Comparison DSL (Happy) is syntactically convenient. DSL can analyse and transform the program; harder with combinators. DSEL (combinators) achieves brevity by extension, using Haskell’s power of abstraction. DSEL is strongly typed, which Happy is not! (Inherits existing type-checker) DSEL permits experimental language design.

What are DSELs used for? A wide variety of applications! 3 Examples: QuickCheck -- software testing Pretty -- pretty-printing data-structures Wash/Cgi -- server side web scripting

QuickCheck The programmer writes properties in the source code, which are tested by a testing tool. The property language is a DSEL! prop_Insert :: Integer -> [Integer] -> Property prop_Insert x xs = ordered xs ==> ordered (insert x xs) prop_Insert :: Integer -> Property prop_Insert x = forAll orderedList $ \xs -> ordered (insert x xs)

Pretty A pretty-printer for binary trees: data Tree a = Leaf | Node a (Tree a) (Tree a) prettyTree Leaf = text “Leaf” prettyTree (Node a left right) = text (“Node ”++show a++“ ”) <> sep [prettyTree left, prettyTree right] <> text “)” (Node 2 (Node 1 Leaf Leaf) (Node 1 Leaf Leaf)) Example: outputs

Wash/CGI A language for defining and processing HTML forms.

Wash/CGI main = run $ ask $ page $ makeForm $ do text "What's your name? " name <- textInputField empty submitField (hello (value name)) (fieldVALUE "Submit") hello (Just name) = htell $ page $ text ("Hello "++name++"!") page x = standardPage "Example" x f $ g $ h $ e = f (g (h e)) Callback function Callback happens in an entirely separate run!

Summary DSLs make applications easy to write, but are (a) costly to design and implement, (b) often rather weak. (e.g. Happy has no function abstraction or type checking) DSELs are almost as convenient to use, but also inherit all the power of Haskell. DSELs are very easy to extend, easy to design and implement, easy to experiment with. DSELs can address a wide variety of application areas.