SlideShare a Scribd company logo
Implementing External DSLs Using Scala Parser Combinators St. Louis Lambda Lounge Sept. 3, 2009 Tim Dalton Senior Software Engineer Object Computing Inc.
External vs Internal DSL Internal DSLs are implemented using syntax of “host” programming language Examples Fluent APIs in Java RSpec and ScalaSpec Constrained by features of programming language External DSLs syntax is only limited by capabilities of the parser
What is a Combinator ? Combinators are functions that can be combined to perform more complex operations Concept originates in Lambda Calculus Mostly comes from the Haskell community Haskell implementations use Monads Scala implementation “almost Monadic”
Scala’s Parser Implementation Context-free LL grammar Left to right Leftmost derivation  Recursive descent Backtracking There are ways to prevent backtracking Advances planned for Scala 2.8 Support for Packrat parsing  Parser Expression Grammar More predictive with less recursion and backtracking
Scala Combinator Parser Hierarchy scala.util.parsing.combinator.Parsers scala.util.parsing.combinator.syntactical.TokenParsers scala.util.parsing.combinator.syntactical.StdTokenParsers scala.util.parsing.combinator.RegexParsers scala.util.parsing.combinator.JavaTokenParsers
A Simple Logo(-Like) Interpreter Only a few commands: Right Turn <angle-degrees>  Left Turn <angle-degrees> Forward <number-of-pixels> Repeat <nested sequence of other commands>
Grammar for Simple Logo forward = (“FORWARD” | “FD”) positive-integer right = (“RIGHT” | “RT) positive-integer left = (“LEFT” | “LT”) positive-integer repeat = “REPEAT” positive-integer “[“{statement}”]” statement = right | left | forward | repeat program = { statement }
Scala Code to Implement Parser object LogoParser extends RegexParsers { def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;r def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger  def right = (&quot;RT&quot;|&quot;RIGHT&quot;)~positiveInteger  def left = (&quot;LT&quot;|&quot;LEFT&quot;)~positiveInteger  def repeat = &quot;REPEAT&quot; ~ positiveInteger ~ &quot;[&quot; ~ rep(statement) ~ &quot;]&quot;  def statement:Parser[Any] = forward | right | left | repeat def program = rep(statement) }
Scala Code to Implement Parser An internal DSL is used to implement an External One Methods on preceding slide are referred to as  parser generators RegexParsers is subclass of Parsers trait that provides a generic parser combinator
A Closer Look def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;r The trailing “r” is a method call the converts the string to a Regex object More verbose syntax: &quot;&quot;&quot;\d+&quot;&quot;&quot;.r() String does not have an r() method !! Class RichString does, so an  implicit conversion  is done
Implicit Conversions One of the more powerful / dangerous features of Scala is implicit conversions RichString.r method signature def  r  : Regex   scala.Predef implicit convertor implicit def stringWrapper( x  : java.lang.String) : RichString   The Scala compiler will look for implicit convertors in scope and insert them implicitly “ With great power, comes great responsibility”
Back to the Parser def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger   The “|” and “~” are methods of class Parsers.Parser[T] !! RegexParser has implicit conversions: implicit def literal(s : String) : Parser[String]  implicit def regex(r : Regex) : Parser[String]   Parser generator methods should return something that can be at least be converted to Parser[T]
Parser[T]’s and ParseResult[T]’s Parsers.Parser[T] Extends Reader => ParseResult[T] This makes it a function object  ParserResult[T] Hierarchy: Parsers.Success Parsers.NoSuccess Parsers.Failure Parsers.Error Invoking Parsers[T] function object return one of the above subclasses
Combining Parser[T]’s Signature for Parser[T].| method: def |[U >: T](q : => Parser[U]) : Parser[U]   Parser Combinator for alternative composition (OR) Succeeds (returns Parsers.Success) if either “this” Parser[T] succeeds or “q” Parser[U] succeeds Type U must be same or super-class of type T.
Combining Parser[T]’s Signature of Parser[T].~ method: def ~[U](p : => Parser[U]) : Parser[~[T, U]] Parser Combinator for sequential composition  Succeeds only if “this” Parser succeeds and “q” Parser succeeds Return an instance “~” that contain both results Yes, “~” is also a class ! Like a Pair, but easier to pattern match on
Forward March Back to the specification of forward: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger   For this combinator to succeed,  Either the Parser for literal “FD” or “FORWARD” And the Parser for the positiveInt Regex Both the literal strings and Regex result of positiveInt are implicitly converted to Parser[String]
Repetition Next lines of note: def repeat = &quot;REPEAT&quot; ~ positiveInteger ~ &quot;[&quot; ~ rep(statement) ~ &quot;]&quot;  def statement:Parser[Any] =    forward | right | left | repeat Type for either repeat or statement need to be explicitly specified due to recursion The rep method specifies that Parser can be repeated
Repetition Signature for Parsers.rep method: def  rep[T](p : => Parser[T]) : Parser[List[T]]   Parser Combinator for repetitions Parses input until Parser, p, fails. Returns consecutive successful results as List.
Other Forms of Repetition def repsep[T](p: => Parser[T], q: => Parser[Any]) : Parser[List[T]] Specifies a Parser to be interleaved in the repetition Example:  repsep(term, &quot;,&quot;)  def rep1[T](p: => Parser[T]): Parser[List[T]] Parses non-empty repetitions def  repN[T](n : Int, p : => Parser[T]) : Parser[List[T]]   Parses a specified number of repetitions
Execution Root Parser Generator: def program = rep(statement)   To Execute the Parser parseAll(program, &quot;REPEAT 4 [FD 100 RT 90]&quot;) Returns Parsers.Success[List[Parsers.~[…]]] Remember ,Parsers.Success[T] is subclass of ParseResult[T] toString: [1.24] parsed: List(((((REPEAT~4)~[)~List((FD~100), (RT~90)))~])) The “…” indicates many levels nested Parsers
Not-so-Happy Path Example of failed Parsing: parseAll(program, &quot;REPEAT 4 [FD 100 RT 90 ) &quot;) Returns Parsers.Failure Subclass of ParseResult[Nothing] toString: [1.23] failure: `]' expected but `)' found REPEAT 4 [FD 100 RT 90) ^ Failure message not always so “precise”
Making Something Useful Successful parse results need to transformed into something that can be evaluated Enter the “eye brows” method of Parser[T]: def  ^^[U](f : (T) => U) : Parser[U]   Parser combinator for function application
Eye Brows Example Example of “^^” method: def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;.r ^^  { x:String => x.toInt } Now positiveInteger generates Parser[Int] instead of Parser[String] Transformer can be shortened to  “{ _.toInt }”
Implementing Commands For the statements we need a hierarchy of command classes: sealed abstract class LogoCommand case class Forward(x: Int) extends LogoCommand case class Turn(x: Int) extends LogoCommand case class Repeat(i: Int, e: List[LogoCommand]) extends LogoCommand
Transforming into Commands The Forward command: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger  ^^  { case _~value => Forward(value) } A ~[String, Int] is being passed in the transformer Pattern matching is to extract the Int, value and construct a Forward instance Forward is a case class, so “new” not needed Case constructs can be partial functions themselves. Longer form: … ^^ { tilde => tilde match  { case _~value => Forward(value) }}
Derivates of “~” Two methods related to “~”: def <~  [U](p: => Parser[U]): Parser[T] Parser combinator for sequential composition which keeps only the left result  def  ~>  [U](p: => Parser[U]): Parser[U]  Parser combinator for sequential composition which keeps only the right result  Note, neither returns a “~” instance The forward method can be simplified: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~>positiveInteger ^^  { Forward(_) }
Updated Parser def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;.r ^^  { _.toInt } def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~>positiveInteger  ^^  { Forward(_) } def right = (&quot;RT&quot;|&quot;RIGHT&quot;)~>positiveInteger ^^  { x => Turn(-x) } def left = (&quot;LT&quot;|&quot;LEFT&quot;)~>positiveInteger ^^  { Turn(_) } def repeat = &quot;REPEAT&quot; ~> positiveInteger ~ &quot;[&quot; ~ rep(statement) <~ &quot;]&quot; ^^ { case number~_~statements => Repeat(number, statements)}
Updated Parser Results Executing the Parser now: parseAll(program, &quot;REPEAT 4 [FD 100 RT 90]&quot;) Results: [1.24] parsed: List(Repeat(4,List(Forward(100), Turn(-90)))) Returns Parsers.Success[List[Repeat]] This can be evaluated !!
Evaluation class LogoEvaluationState { var x = 0 var y = 0 var heading = 0 } implicit def dblToInt(d: Double):Int = if (d > 0) (d+0.5).toInt else (d-0.5).toInt def parse(s: String) : List[LogoCommand] = LogoParser.parse(s).get def evaluate(parseResult: LogoParser.ParseResult[List[LogoCommand]], g:Graphics2D) { var state = new LogoEvaluationState if (parseResult.successful) { evaluate(parseResult.get, g, state) } // draw turtle evaluate(parse(&quot;RT 90 FD 3 LT 110 FD 10 LT 140 FD 10 LT 110 FD 3&quot;),  g, state) } // Continued...
Evaluation (More Functional) private def evaluate(list: List[LogoCommand], g:Graphics2D, state:LogoEvaluationState) {  if (!list.isEmpty) { val head :: tail = list head match {   case Forward(distance) => { val (nextX, nextY) =  (state.x + distance * sin(toRadians(state.heading)),   state.y + distance * cos(toRadians(state.heading))) g.drawLine(state.x, state.y, nextX, nextY) state.x = nextX state.y = nextY evaluate(tail, g, state)   }   case Turn(degrees) => { state.heading += degrees evaluate(tail, g, state)   }   case Repeat(0, _) => evaluate(tail, g, state)   case Repeat(count, statements) =>  evaluate(statements ::: Repeat(count-1, statements)::tail, g, state) } } }
Evaluation (More Imperative) def evaluate(list: List[LogoCommand], g:Graphics2D, state:LogoEvaluationState) { list.foreach(evaluate(_, g, state)) } def evaluate(command:LogoCommand, g:Graphics2D, state:LogoEvaluationState) { command match {   case Forward(distance) => {     val (nextX, nextY) = (state.x + distance *  Math.sin(Math.toRadians(state.heading)), state.y + distance *  Math.cos(Math.toRadians(state.heading))) g.drawLine(state.x, state.y, nextX, nextY) state.x = nextX state.y = nextY   }   case Turn(degrees) => state.heading += degrees   case Repeat(count, statements) => (0 to count).foreach { _ => evaluate(statements, g, state)     }   } }
Demonstration

More Related Content

What's hot (19)

PDF
Topdown parsing
Antony Alex
 
PPT
Functions
PatriciaPabalan
 
PDF
Operator precedence
Akshaya Arunan
 
PPTX
Types of Parser
SomnathMore3
 
PPTX
Quicksort Presentation
irdginfo
 
PPT
Module 11
bittudavis
 
PPTX
Top Down Parsing, Predictive Parsing
Tanzeela_Hussain
 
DOCX
Nonrecursive predictive parsing
alldesign
 
PDF
Recursive descent parsing
Boy Baukema
 
DOC
Parsing
ShrikantSharma86
 
PDF
Left factor put
siet_pradeep18
 
PPTX
Parsing in Compiler Design
Akhil Kaushik
 
PDF
Python (regular expression)
Chirag Shetty
 
PDF
Bottom up parser
Akshaya Arunan
 
PPTX
Control Structures in C
sana shaikh
 
PDF
Theory of automata and formal language lab manual
Nitesh Dubey
 
PPTX
Top down and botttom up Parsing
Gerwin Ocsena
 
Topdown parsing
Antony Alex
 
Functions
PatriciaPabalan
 
Operator precedence
Akshaya Arunan
 
Types of Parser
SomnathMore3
 
Quicksort Presentation
irdginfo
 
Module 11
bittudavis
 
Top Down Parsing, Predictive Parsing
Tanzeela_Hussain
 
Nonrecursive predictive parsing
alldesign
 
Recursive descent parsing
Boy Baukema
 
Left factor put
siet_pradeep18
 
Parsing in Compiler Design
Akhil Kaushik
 
Python (regular expression)
Chirag Shetty
 
Bottom up parser
Akshaya Arunan
 
Control Structures in C
sana shaikh
 
Theory of automata and formal language lab manual
Nitesh Dubey
 
Top down and botttom up Parsing
Gerwin Ocsena
 

Viewers also liked (20)

PPT
Writing DSL's in Scala
Abhijit Sharma
 
PPT
Virtualization
turnerwife
 
PPT
About Blogs
guest25061d
 
PPT
PO
cumba2010
 
PDF
Downtown Ferndale Business Guide 2011
Ferndale Downtown Development Authority
 
PPTX
Markaların Logo Değişimleri
Yunus Emre
 
PPT
Coches Fantasticos
josevlc
 
PPTX
NEDMAInno14: The Fundamental Things Still Apply: How to Deal with "Innovation...
New England Direct Marketing Association
 
PPTX
MTech13: "How to Jump Start Sales Productivity with Content" - Paula Crerar
New England Direct Marketing Association
 
PDF
Yahoo Korea 10th anniversary Plan Concept
Jongjin Park
 
PPT
Google Places - Global Approach ISS 2012
Lisa Myers
 
PPS
Учебные форматы МАСП
Sun Way International Academy
 
PDF
A Redesign of VisitPhilly.com by Happy Cog
Kevin Hoffman
 
PDF
everdo for ad
zopen
 
PPTX
CEO Roundtable on Local and State Tax Laws
Wichita Metro Chamber of Commerce
 
PDF
MTech13: "The Power of Marketing Automation in an Inbound World" - Nick Salva...
New England Direct Marketing Association
 
PDF
Istant report Open Spece Technology "Facciamo il Macello"
Conetica
 
PDF
Print Technology: Functional Printed Electronics - RFID & NFC versus QR Codes...
New England Direct Marketing Association
 
PDF
Workflow NPW2010
Jonas Brømsø
 
PDF
Илья Бирман – Ангстрем
404fest
 
Writing DSL's in Scala
Abhijit Sharma
 
Virtualization
turnerwife
 
About Blogs
guest25061d
 
Downtown Ferndale Business Guide 2011
Ferndale Downtown Development Authority
 
Markaların Logo Değişimleri
Yunus Emre
 
Coches Fantasticos
josevlc
 
NEDMAInno14: The Fundamental Things Still Apply: How to Deal with "Innovation...
New England Direct Marketing Association
 
MTech13: "How to Jump Start Sales Productivity with Content" - Paula Crerar
New England Direct Marketing Association
 
Yahoo Korea 10th anniversary Plan Concept
Jongjin Park
 
Google Places - Global Approach ISS 2012
Lisa Myers
 
Учебные форматы МАСП
Sun Way International Academy
 
A Redesign of VisitPhilly.com by Happy Cog
Kevin Hoffman
 
everdo for ad
zopen
 
CEO Roundtable on Local and State Tax Laws
Wichita Metro Chamber of Commerce
 
MTech13: "The Power of Marketing Automation in an Inbound World" - Nick Salva...
New England Direct Marketing Association
 
Istant report Open Spece Technology "Facciamo il Macello"
Conetica
 
Print Technology: Functional Printed Electronics - RFID & NFC versus QR Codes...
New England Direct Marketing Association
 
Workflow NPW2010
Jonas Brømsø
 
Илья Бирман – Ангстрем
404fest
 
Ad

Similar to Implementing External DSLs Using Scala Parser Combinators (20)

PDF
TI1220 Lecture 9: Parsing & interpretation
Eelco Visser
 
PDF
Parsers Combinators in Scala, Ilya @lambdamix Kliuchnikov
Vasil Remeniuk
 
PDF
Combinator parsing
Swanand Pagnis
 
ODP
Beginning Scala Svcc 2009
David Pollak
 
PPT
Regular Expression
Minh Hoang
 
PPTX
6-Practice Problems - LL(1) parser-16-05-2023.pptx
venkatapranaykumarGa
 
PPT
Regular expression made by To Minh Hoang - Portal team
Thuy_Dang
 
PPT
JBUG 11 - Scala For Java Programmers
Tikal Knowledge
 
PPTX
Oscon 2010 Specs talk
Eric Torreborre
 
PPT
PARSING.ppt
ayyankhanna6480086
 
PDF
Assignment10
Sunita Milind Dol
 
PPTX
Scala Parser Combinators - Scalapeno Lightning Talk
Lior Schejter
 
PDF
Scala by Luc Duponcheel
Stephan Janssen
 
PDF
Learn from LL(1) to PEG parser the hard way
Kir Chou
 
PDF
Scala Functional Patterns
league
 
PPTX
Parsers -
Wael Badawy
 
ODP
Scala introduction
Alf Kristian Støyle
 
PPT
Scala presentation by Aleksandar Prokopec
Loïc Descotte
 
PPTX
Compiler Design_Syntax Analyzer_Top Down Parsers.pptx
RushaliDeshmukh2
 
PPTX
Syntax Analysis.pptx
AshaS74
 
TI1220 Lecture 9: Parsing & interpretation
Eelco Visser
 
Parsers Combinators in Scala, Ilya @lambdamix Kliuchnikov
Vasil Remeniuk
 
Combinator parsing
Swanand Pagnis
 
Beginning Scala Svcc 2009
David Pollak
 
Regular Expression
Minh Hoang
 
6-Practice Problems - LL(1) parser-16-05-2023.pptx
venkatapranaykumarGa
 
Regular expression made by To Minh Hoang - Portal team
Thuy_Dang
 
JBUG 11 - Scala For Java Programmers
Tikal Knowledge
 
Oscon 2010 Specs talk
Eric Torreborre
 
PARSING.ppt
ayyankhanna6480086
 
Assignment10
Sunita Milind Dol
 
Scala Parser Combinators - Scalapeno Lightning Talk
Lior Schejter
 
Scala by Luc Duponcheel
Stephan Janssen
 
Learn from LL(1) to PEG parser the hard way
Kir Chou
 
Scala Functional Patterns
league
 
Parsers -
Wael Badawy
 
Scala introduction
Alf Kristian Støyle
 
Scala presentation by Aleksandar Prokopec
Loïc Descotte
 
Compiler Design_Syntax Analyzer_Top Down Parsers.pptx
RushaliDeshmukh2
 
Syntax Analysis.pptx
AshaS74
 
Ad

Recently uploaded (20)

PDF
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
HubSpot Main Hub: A Unified Growth Platform
Jaswinder Singh
 
PDF
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PDF
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
PDF
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
PDF
Blockchain Transactions Explained For Everyone
CIFDAQ
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PDF
Presentation - Vibe Coding The Future of Tech
yanuarsinggih1
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
HubSpot Main Hub: A Unified Growth Platform
Jaswinder Singh
 
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
Blockchain Transactions Explained For Everyone
CIFDAQ
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
Presentation - Vibe Coding The Future of Tech
yanuarsinggih1
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 

Implementing External DSLs Using Scala Parser Combinators

  • 1. Implementing External DSLs Using Scala Parser Combinators St. Louis Lambda Lounge Sept. 3, 2009 Tim Dalton Senior Software Engineer Object Computing Inc.
  • 2. External vs Internal DSL Internal DSLs are implemented using syntax of “host” programming language Examples Fluent APIs in Java RSpec and ScalaSpec Constrained by features of programming language External DSLs syntax is only limited by capabilities of the parser
  • 3. What is a Combinator ? Combinators are functions that can be combined to perform more complex operations Concept originates in Lambda Calculus Mostly comes from the Haskell community Haskell implementations use Monads Scala implementation “almost Monadic”
  • 4. Scala’s Parser Implementation Context-free LL grammar Left to right Leftmost derivation Recursive descent Backtracking There are ways to prevent backtracking Advances planned for Scala 2.8 Support for Packrat parsing Parser Expression Grammar More predictive with less recursion and backtracking
  • 5. Scala Combinator Parser Hierarchy scala.util.parsing.combinator.Parsers scala.util.parsing.combinator.syntactical.TokenParsers scala.util.parsing.combinator.syntactical.StdTokenParsers scala.util.parsing.combinator.RegexParsers scala.util.parsing.combinator.JavaTokenParsers
  • 6. A Simple Logo(-Like) Interpreter Only a few commands: Right Turn <angle-degrees> Left Turn <angle-degrees> Forward <number-of-pixels> Repeat <nested sequence of other commands>
  • 7. Grammar for Simple Logo forward = (“FORWARD” | “FD”) positive-integer right = (“RIGHT” | “RT) positive-integer left = (“LEFT” | “LT”) positive-integer repeat = “REPEAT” positive-integer “[“{statement}”]” statement = right | left | forward | repeat program = { statement }
  • 8. Scala Code to Implement Parser object LogoParser extends RegexParsers { def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;r def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger def right = (&quot;RT&quot;|&quot;RIGHT&quot;)~positiveInteger def left = (&quot;LT&quot;|&quot;LEFT&quot;)~positiveInteger def repeat = &quot;REPEAT&quot; ~ positiveInteger ~ &quot;[&quot; ~ rep(statement) ~ &quot;]&quot; def statement:Parser[Any] = forward | right | left | repeat def program = rep(statement) }
  • 9. Scala Code to Implement Parser An internal DSL is used to implement an External One Methods on preceding slide are referred to as parser generators RegexParsers is subclass of Parsers trait that provides a generic parser combinator
  • 10. A Closer Look def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;r The trailing “r” is a method call the converts the string to a Regex object More verbose syntax: &quot;&quot;&quot;\d+&quot;&quot;&quot;.r() String does not have an r() method !! Class RichString does, so an implicit conversion is done
  • 11. Implicit Conversions One of the more powerful / dangerous features of Scala is implicit conversions RichString.r method signature def r : Regex scala.Predef implicit convertor implicit def stringWrapper( x : java.lang.String) : RichString The Scala compiler will look for implicit convertors in scope and insert them implicitly “ With great power, comes great responsibility”
  • 12. Back to the Parser def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger The “|” and “~” are methods of class Parsers.Parser[T] !! RegexParser has implicit conversions: implicit def literal(s : String) : Parser[String] implicit def regex(r : Regex) : Parser[String] Parser generator methods should return something that can be at least be converted to Parser[T]
  • 13. Parser[T]’s and ParseResult[T]’s Parsers.Parser[T] Extends Reader => ParseResult[T] This makes it a function object ParserResult[T] Hierarchy: Parsers.Success Parsers.NoSuccess Parsers.Failure Parsers.Error Invoking Parsers[T] function object return one of the above subclasses
  • 14. Combining Parser[T]’s Signature for Parser[T].| method: def |[U >: T](q : => Parser[U]) : Parser[U] Parser Combinator for alternative composition (OR) Succeeds (returns Parsers.Success) if either “this” Parser[T] succeeds or “q” Parser[U] succeeds Type U must be same or super-class of type T.
  • 15. Combining Parser[T]’s Signature of Parser[T].~ method: def ~[U](p : => Parser[U]) : Parser[~[T, U]] Parser Combinator for sequential composition Succeeds only if “this” Parser succeeds and “q” Parser succeeds Return an instance “~” that contain both results Yes, “~” is also a class ! Like a Pair, but easier to pattern match on
  • 16. Forward March Back to the specification of forward: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger For this combinator to succeed, Either the Parser for literal “FD” or “FORWARD” And the Parser for the positiveInt Regex Both the literal strings and Regex result of positiveInt are implicitly converted to Parser[String]
  • 17. Repetition Next lines of note: def repeat = &quot;REPEAT&quot; ~ positiveInteger ~ &quot;[&quot; ~ rep(statement) ~ &quot;]&quot; def statement:Parser[Any] = forward | right | left | repeat Type for either repeat or statement need to be explicitly specified due to recursion The rep method specifies that Parser can be repeated
  • 18. Repetition Signature for Parsers.rep method: def rep[T](p : => Parser[T]) : Parser[List[T]] Parser Combinator for repetitions Parses input until Parser, p, fails. Returns consecutive successful results as List.
  • 19. Other Forms of Repetition def repsep[T](p: => Parser[T], q: => Parser[Any]) : Parser[List[T]] Specifies a Parser to be interleaved in the repetition Example: repsep(term, &quot;,&quot;) def rep1[T](p: => Parser[T]): Parser[List[T]] Parses non-empty repetitions def repN[T](n : Int, p : => Parser[T]) : Parser[List[T]] Parses a specified number of repetitions
  • 20. Execution Root Parser Generator: def program = rep(statement) To Execute the Parser parseAll(program, &quot;REPEAT 4 [FD 100 RT 90]&quot;) Returns Parsers.Success[List[Parsers.~[…]]] Remember ,Parsers.Success[T] is subclass of ParseResult[T] toString: [1.24] parsed: List(((((REPEAT~4)~[)~List((FD~100), (RT~90)))~])) The “…” indicates many levels nested Parsers
  • 21. Not-so-Happy Path Example of failed Parsing: parseAll(program, &quot;REPEAT 4 [FD 100 RT 90 ) &quot;) Returns Parsers.Failure Subclass of ParseResult[Nothing] toString: [1.23] failure: `]' expected but `)' found REPEAT 4 [FD 100 RT 90) ^ Failure message not always so “precise”
  • 22. Making Something Useful Successful parse results need to transformed into something that can be evaluated Enter the “eye brows” method of Parser[T]: def ^^[U](f : (T) => U) : Parser[U] Parser combinator for function application
  • 23. Eye Brows Example Example of “^^” method: def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;.r ^^ { x:String => x.toInt } Now positiveInteger generates Parser[Int] instead of Parser[String] Transformer can be shortened to “{ _.toInt }”
  • 24. Implementing Commands For the statements we need a hierarchy of command classes: sealed abstract class LogoCommand case class Forward(x: Int) extends LogoCommand case class Turn(x: Int) extends LogoCommand case class Repeat(i: Int, e: List[LogoCommand]) extends LogoCommand
  • 25. Transforming into Commands The Forward command: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~positiveInteger ^^ { case _~value => Forward(value) } A ~[String, Int] is being passed in the transformer Pattern matching is to extract the Int, value and construct a Forward instance Forward is a case class, so “new” not needed Case constructs can be partial functions themselves. Longer form: … ^^ { tilde => tilde match { case _~value => Forward(value) }}
  • 26. Derivates of “~” Two methods related to “~”: def <~ [U](p: => Parser[U]): Parser[T] Parser combinator for sequential composition which keeps only the left result def ~> [U](p: => Parser[U]): Parser[U] Parser combinator for sequential composition which keeps only the right result Note, neither returns a “~” instance The forward method can be simplified: def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~>positiveInteger ^^ { Forward(_) }
  • 27. Updated Parser def positiveInteger = &quot;&quot;&quot;\d+&quot;&quot;&quot;.r ^^ { _.toInt } def forward = (&quot;FD&quot;|&quot;FORWARD&quot;)~>positiveInteger ^^ { Forward(_) } def right = (&quot;RT&quot;|&quot;RIGHT&quot;)~>positiveInteger ^^ { x => Turn(-x) } def left = (&quot;LT&quot;|&quot;LEFT&quot;)~>positiveInteger ^^ { Turn(_) } def repeat = &quot;REPEAT&quot; ~> positiveInteger ~ &quot;[&quot; ~ rep(statement) <~ &quot;]&quot; ^^ { case number~_~statements => Repeat(number, statements)}
  • 28. Updated Parser Results Executing the Parser now: parseAll(program, &quot;REPEAT 4 [FD 100 RT 90]&quot;) Results: [1.24] parsed: List(Repeat(4,List(Forward(100), Turn(-90)))) Returns Parsers.Success[List[Repeat]] This can be evaluated !!
  • 29. Evaluation class LogoEvaluationState { var x = 0 var y = 0 var heading = 0 } implicit def dblToInt(d: Double):Int = if (d > 0) (d+0.5).toInt else (d-0.5).toInt def parse(s: String) : List[LogoCommand] = LogoParser.parse(s).get def evaluate(parseResult: LogoParser.ParseResult[List[LogoCommand]], g:Graphics2D) { var state = new LogoEvaluationState if (parseResult.successful) { evaluate(parseResult.get, g, state) } // draw turtle evaluate(parse(&quot;RT 90 FD 3 LT 110 FD 10 LT 140 FD 10 LT 110 FD 3&quot;), g, state) } // Continued...
  • 30. Evaluation (More Functional) private def evaluate(list: List[LogoCommand], g:Graphics2D, state:LogoEvaluationState) { if (!list.isEmpty) { val head :: tail = list head match { case Forward(distance) => { val (nextX, nextY) = (state.x + distance * sin(toRadians(state.heading)), state.y + distance * cos(toRadians(state.heading))) g.drawLine(state.x, state.y, nextX, nextY) state.x = nextX state.y = nextY evaluate(tail, g, state) } case Turn(degrees) => { state.heading += degrees evaluate(tail, g, state) } case Repeat(0, _) => evaluate(tail, g, state) case Repeat(count, statements) => evaluate(statements ::: Repeat(count-1, statements)::tail, g, state) } } }
  • 31. Evaluation (More Imperative) def evaluate(list: List[LogoCommand], g:Graphics2D, state:LogoEvaluationState) { list.foreach(evaluate(_, g, state)) } def evaluate(command:LogoCommand, g:Graphics2D, state:LogoEvaluationState) { command match { case Forward(distance) => { val (nextX, nextY) = (state.x + distance * Math.sin(Math.toRadians(state.heading)), state.y + distance * Math.cos(Math.toRadians(state.heading))) g.drawLine(state.x, state.y, nextX, nextY) state.x = nextX state.y = nextY } case Turn(degrees) => state.heading += degrees case Repeat(count, statements) => (0 to count).foreach { _ => evaluate(statements, g, state) } } }

Editor's Notes

  • #8: (|) = OR logic { } = repetition
  • #11: The multiline string notation is used so escaping “\\” in the regex is not needed
  • #12: Internal DSLs in Scala leverage implicit conversion to a great degree to allow a more flexible “syntax”
  • #16: ~ is a case class.
  • #18: Because statement and repeat invoke each other and therefore can be invoked recursively, The return type for one must be specified
  • #20: There is also rep1sep
  • #22: Demonstrate no so precise message “RT” -&gt; “ET”
  • #28: For some reason Scala compile didn’t like { Turn(-_) }
  • #31: Tail Recursive !!