-
Notifications
You must be signed in to change notification settings - Fork 15
Understanding the API
In Parsley, everything resides within the parsley
package, and the major entry point is
parsley.Parsley
.
There are a few modules of note:
-
parsley.Parsley
: contains some of the basic and primitive combinators (at least those that aren't methods on parsers). -
parsley.combinator
: contains handy combinators, this should be your first port of call when you want to do something but are not sure a combinator exists for it. At the very least, theeof
combinator is very common. -
parsley.character
: contains a variety of combinators which deal with characters, key ones includechar
,satisfy
andstring
. -
parsley.implicits
: contains the very useful implicit conversion combinators. In particular, importingcharLift
andstringLift
allows you write character and string literals as if they were parsers themselves. There are also implicit classes here which extend functions of any arity with a corresponding.lift
method, instead of using theliftN
functions. -
parsley.expr
: contains the machinery needed to generate expression parsers for you based, at its simplest, on a table of operators in order of precedence. This is well worth a look (this is covered in detail in: Building Expression Parsers).
Other than these, the class
parsley.token.Lexer
contains useful functionality when instantiated with a
parsley.token.LanguageDef
instance. However, most of the combinators it provides as part of this package are tailored to
the original Parsec library, so integer literals and string literals are parsed according to the
Haskell specification. However, many of the combinators are fully configurable, and, in many
cases, have been heavily optimised.
You may notice, if you visit the Parsley[A]
class itself that it doesn't have many methods at all:
only those that run the parser as well as a handful of methods that you are probably unlikely to use.
Where are the combinators?! Well, to support recursive parsers in the nicest way possible, Parsley needs
to keep the combinators as lazy as possible. Method invocation is strict in the receiver, which is too
strict for Parsley's needs. To that end, Parsley uses implicit classes to provide the functionality
via extension methods (and at the same time creates a lazy receiver). These implicit classes can be found
in the parsley.Parsley
object and are as follows:
-
parsley.Parsley.LazyChooseParsley
: defines the ternary combinator?:
, where the receiver is the right-hand side (this is the effect of a trailing:
in Scala's names). -
parsley.Parsley.MapParsley
: contains the<#>
combinator but where the function is on the left-hand side. -
parsley.Parsley.LazyParsley
: this is the place to go and find the vast majority of the operator and method style combinators. It's so important it needs its own subsection!
The first thing to note about LazyParsley
is that its type is perhaps a bit strange. It provides
additional functionality to a value of type P
. This seems strange at first, but observe that the
class itself also takes an implicit function of type P => Parsley[A]
. This means that LazyParsley
can provide the combinator functionality to any type, so long as that type P
is implicitly convertible to
a parser. Let's take a concrete example:
import parsley.Parsley.LazyParsley
import parsley.character.char
import parsley.implicits.charLift
val p/*: Parsley[Int]*/ = char('a') #> 7
val q/*: Parsley[Int]*/ = 'a' #> 7
println('c'.runParser("c"))
How is it possible that #>
worked on Char
? Well, the Scala compiler will elaborate this into
the following program:
import parsley.Parsley.LazyParsley
import parsley.character.char
import parsley.implicits.charLift
val p = new LazyParsley(char('a'))(identity[Parsley[Char]]) #> 7
val q = new LazyParsley('a')(charLift) #> 7
println(charLift('c').runParser("c"))
Fear not: Scala makes an effort not to construct implicit classes at runtime, so the
LazyParsley
class has little overhead. It's more like
LazyParsley.extension_#>(charLift('a'), 7)
in principal. This code works because both
parsley.implicits.charLift
and parsley.Parsley.LazyParsley
were imported and thus in scope for
implicit resolution. However, you may write your own implicit conversion from other types
(or even for Char
) to add additional functionality and tailor to your needs. You can also write
your own method based combinators using your own implicit class similar to this one.
Don't worry too much if you don't understand how this works under the hood, just be aware that the
"operator" combinators are provided by the LazyParsley
class, and you can find them within its
documentation.
The Wiki refers to the latest version in the parsley-4.x.y
series.