+ All Categories
Home > Technology > Parser combinators

Parser combinators

Date post: 15-Jan-2015
Category:
Upload: lifecoder
View: 3,416 times
Download: 2 times
Share this document with a friend
Description:
Parsec parser combinators by Denis Yermakov. http://twitter.com/lifecoder
Popular Tags:
50
Parser Combinators Writing external DSLs Wednesday, November 9, 2011
Transcript
Page 1: Parser combinators

Parser CombinatorsWriting external DSLs

Wednesday, November 9, 2011

Page 2: Parser combinators

DSLs

• DSL is an ultimate abstraction

• You create a language that precisely describes the problem you are trying to solve.

Wednesday, November 9, 2011

Page 3: Parser combinators

Internal & External

• DSL can be internal - made using the host programming language.

• Or external - you invent a little language for solving your problem.

Wednesday, November 9, 2011

Page 4: Parser combinators

Internal DSL: Good

• No external tool dependencies

• Nice IDE support

• Type checking for free

• Easy to implement

• Gives full power of your language

• Parsing is free

Wednesday, November 9, 2011

Page 5: Parser combinators

Internal DSL: Bad

• It requires expressive language

• If the language is not expressive enough DSL will be verbose and not pleasant to use

• It can produce obscure error messages

Wednesday, November 9, 2011

Page 6: Parser combinators

External DSL: Good

• Platform independent

• Nicer syntax

• External tool can perform static analysis

• Good error messages

Wednesday, November 9, 2011

Page 7: Parser combinators

External DSLs: Bad

• No IDE support

• Hard to implement

Wednesday, November 9, 2011

Page 8: Parser combinators

External DSLs

• External DSLs can be implemented using interpretation or code generation.

• Parse

• Analyze

• Interpret | Generate Code

Wednesday, November 9, 2011

Page 9: Parser combinators

parseJSON :: String -> Either Error JSONObject

Wednesday, November 9, 2011

Page 10: Parser combinators

JSON Grammar-like stuff

object := '{' fields? '}'fields := field (',' field)*field := id ':' valuevalue := literal | object | array

Wednesday, November 9, 2011

Page 11: Parser combinators

Bison (C)ANTLR (Java and many others)

You can encode grammar by hand

Wednesday, November 9, 2011

Page 12: Parser combinators

function parseObject() { match('{') if (lookahead(ID)) parseFields() match('}')}

Wednesday, November 9, 2011

Page 13: Parser combinators

function parseFields() { parseField() while (lookahead(',')) { consume() parseField() }}

Wednesday, November 9, 2011

Page 14: Parser combinators

• Parser can either consume some input or not.

• Parser can fail or succeed.

• Parser can return some value.

Wednesday, November 9, 2011

Page 15: Parser combinators

• Usually sequences are just sequential calls.

• Optional parts of grammar are just IFs

• Zero or more elements is WHILE

• Grammar rule is a function

Wednesday, November 9, 2011

Page 16: Parser combinators

function parseFields() { separatedBy(',', parseField)}

function separatedBy(ch, f) { f() while (lookahead(ch)) { consume() f() }}

Wednesday, November 9, 2011

Page 17: Parser combinators

function sepBy(ch, p) { return seq(p, many(seq(ch, p)))}

Wednesday, November 9, 2011

Page 18: Parser combinators

Combinator pattern

Wednesday, November 9, 2011

Page 19: Parser combinators

Haskell in 60 seconds

Wednesday, November 9, 2011

Page 20: Parser combinators

main = putStrLn "Hello World!"

times x n = x * ntimes = \x n -> x * ntimes = \x -> \n -> x * ntimes = (*)times 2 32 `times` 3data Point = Point Int Intdata Either a b = Left a | Right bPoint 10 20Left “Hello”

Wednesday, November 9, 2011

Page 21: Parser combinators

import Text.Parsec

parse :: Parser -> SourceName -> String -> Either ParseError a

Wednesday, November 9, 2011

Page 22: Parser combinators

p = satisfy (\x -> x == 'a')main = print (parse p "<test>" "abc")

> Right 'a'

p = satisfy (\x -> x == 'a')main = print (parse p "<test>" "bcd")

> Left "<test>" (line 1, column 1):> unexpected "b"

Wednesday, November 9, 2011

Page 23: Parser combinators

-- There is something like this in Parsecchar c = satisfy (\x -> x == c)

-- Examplep = char 'a'main = print (parse p "<test>" "bcd")

> Left "<test>" (line 1, column 1):> unexpected "b"> expecting "a"

Wednesday, November 9, 2011

Page 24: Parser combinators

char c = satisfy (==c) <?> show [c]

Wednesday, November 9, 2011

Page 25: Parser combinators

p = char 'a' >>= \x -> char 'b' >>= \y -> return [x, y]main = print (parse p "<test>" "abc")

> Right "ab"

Wednesday, November 9, 2011

Page 26: Parser combinators

p = do char 'a' char 'b' return "ab"main = print (parse p "<test>" "abc")

> Right "ab"

Wednesday, November 9, 2011

Page 27: Parser combinators

p = do char 'a' char 'b' <|> char 'q' char 'c' <|> char 'd'

main = print (parse p "<test>" "abc")

Wednesday, November 9, 2011

Page 28: Parser combinators

letter = satisfy isAlpha <?> "letter"digit = satisfy isDigit <?> "digit"spaces = skipMany space <?> "white space"

many p = ... censored ...

Wednesday, November 9, 2011

Page 29: Parser combinators

p = do letter many (letter <|> digit)main = print (parse p "<test>" "hello123")

> Right "ello123"

Wednesday, November 9, 2011

Page 30: Parser combinators

p = do x <- letter xs <- many (letter <|> digit) return (x:xs)main = print (parse p "<test>" "hello123")

> Right "hello123"

Wednesday, November 9, 2011

Page 31: Parser combinators

ident = do x <- letter xs <- many (letter <|> digit) return (x:xs)p = identmain = print (parse p "<test>" "123hello")

> Left "<test>" (line 1, column 1):> unexpected "1"> expecting letter

Wednesday, November 9, 2011

Page 32: Parser combinators

ident = do x <- letter xs <- many (letter <|> digit) return (x:xs)p = ident <?> "variable name"main = print (parse p "<test>" "123hello")

> Left "<test>" (line 1, column 1):> unexpected "1"> expecting variable name

Wednesday, November 9, 2011

Page 33: Parser combinators

ident = do x <- letter xs <- many (letter <|> digit) return (x:xs) <?> "variable name"p = ident main = print (parse p "<test>" "123hello")

Wednesday, November 9, 2011

Page 34: Parser combinators

ident = do x <- letter xs <- many (letter <|> digit) return (x:xs) <?> "variable name"letKeyword = string "let"p = ident <|> letKeywordmain = print (parse p "<test>" "letter")

> Right "letter"

Wednesday, November 9, 2011

Page 35: Parser combinators

ident = do x <- letter xs <- many (letter <|> digit) return (x:xs) <?> "variable name"letKeyword = do s <- string "let" notFollowedBy (letter <|> digit) spaces return sp = try(letKeyword) <|> identmain = print (parse p "<test>" "letter")-----Right "letter"

Wednesday, November 9, 2011

Page 36: Parser combinators

Lexer-like example

Wednesday, November 9, 2011

Page 37: Parser combinators

type Name = Stringdata Expr = Number Integer | Var Name | Let Name Expr | Seq [Expr] deriving Show

Wednesday, November 9, 2011

Page 38: Parser combinators

myDef = emptyDef { identStart = letter <|> char '_', identLetter = alphaNum <|> char '_', opStart = opLetter myDef, opLetter = oneOf "=,;", reservedOpNames = [ ",", ";", "=" ], reservedNames = ["let"], caseSensitive = True }

Wednesday, November 9, 2011

Page 39: Parser combinators

tokenParser = PT.makeTokenParser myDef

parens = PT.parens tokenParsernaturalNumber = PT.natural tokenParserkeyword = PT.reserved tokenParseridentifier = PT.identifier tokenParserop = PT.reservedOp tokenParsercommaSep = PT.commaSep tokenParsercommaSep1 = PT.commaSep1 tokenParser

Wednesday, November 9, 2011

Page 40: Parser combinators

simple = numberLiteral <|> var

numberLiteral = do n <- naturalNumber return $ Number n

var = do s <- identifier return $ Var s

Wednesday, November 9, 2011

Page 41: Parser combinators

letStmt = do keyword "let" defs <- commaSep1 def op ";" return $ Seq defs def = do name <- identifier op "=" value <- simple return $ Let name value

Wednesday, November 9, 2011

Page 42: Parser combinators

main = print (parse letStmt "<test>" "let one = 1, two = 2;"

)

----

Right (Seq [Let "one" (Number 1),Let "two" (Number 2)

])

Wednesday, November 9, 2011

Page 43: Parser combinators

CSV Parser Example

Wednesday, November 9, 2011

Page 44: Parser combinators

import Text.Parsec

csvFile = line `endBy1` eolline = cell `sepBy` (char ',')cell = wsopt >> many (noneOf ",\n")wsopt = many (oneOf " \t")eol = char '\n'main = print (parse csvFile "<test>" input) where input = "aa, b,c\nd,e,f\n"

> Right [["aa","b","c"],["d","e","f"]]

Wednesday, November 9, 2011

Page 45: Parser combinators

import scala.util.parsing.combinator._

object CSVParser extends RegexParsers { override def skipWhitespace = false def whitespace = """[ \t]*"""r def csv = rep1sep(row, "\n") <~ "\n" def row = rep1sep(field, ",") def field = opt(whitespace) ~> """[^\n,]""".r def parse(s: String) = parseAll(csv, s) }println(CSVParser.parse("a,b,c\nd,e, f\n"))

> [3.1] parsed: List(List(a, b, c), List(d, e, f))

Wednesday, November 9, 2011

Page 46: Parser combinators

Pysec for PythonSpirit for C++In standard library of ScalaA lot of others...

Wednesday, November 9, 2011

Page 47: Parser combinators

Stuff to read

• Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages by Terence Parr

Wednesday, November 9, 2011

Page 48: Parser combinators

Stuff to read

• Domain Specific Languages by Martin Fowler

Wednesday, November 9, 2011

Page 49: Parser combinators

Stuff to read

• http://learnyouahaskell.com/Learn You a Haskell for a Great Good

Wednesday, November 9, 2011

Page 50: Parser combinators

The End

Wednesday, November 9, 2011


Recommended