An opaque type based on
Pratt.Advanced.Config
representing the parser configuration.
A Config is created using configure.
Build a Config from NUD, LED and spaces parsers.
nuds:
A list of "Null Denotation" parsers that do not take an expression on the left. They will be tried successively by the parser using
Parser.oneOfand the parserConfig.
Examples: parsers for constants, literals, prefix expressions, function calls or a sub-expression between parentheses.
leds:
A list of "Left Denotation" parsers that do take an expression on the left and have an
Intbinding power. The ones that have a binding power above the current one (0by default) will be tried by the parser usingParser.oneOf, the parserConfigand the current left expression (the expression returned by the previous parser).
Examples: parsers for prefix and postfix expressions.
spaces:
A parser called before and after each
nudandledparser, usually used to consume whitespaces.
If a more specific behavior is needed, this parser can be ignored by using
succeed ()and a custom behavior added insidenudsandledsparsers.
Notes:
NUD and LED parsers are parameterized by a Config
to be able to call expression and
subExpression, which are the main building
blocks for NUD and LED helpers. This Config will be automatically
passed by the parser.backtrackable, so it is up to you to correctly
setup your parsers to avoid having failed parsers consuming characters.For example, a basic calculator could be configured like this:
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Float -> Parser Float)
nuds =
[ always float
, prefix 3 (symbol "-") negate
, \config ->
succeed identity
|. symbol "("
|= expression config
|. symbol ")"
]
leds : List (Config Float -> ( Int, Float -> Parser Float ))
leds =
[ infixLeft 1 (symbol "+") (+)
, infixLeft 1 (symbol "-") (-)
, infixLeft 2 (symbol "*") (*)
, infixLeft 2 (symbol "/") (/)
, infixRight 4 (symbol "^") (^)
]
conf : Config Float
conf =
configure
{ nuds = nuds
, leds = leds
, spaces = spaces
}
parser : Parser Float
parser =
succeed identity
|= expression conf
|. end
run parser "-1*3--5+4/2^2" --> Ok ((-1*3)-(-5)+(4/(2^2)))
run parser "-1*3--5+4/2^2" --> Ok 3
run parser "((-1*3)-(-5)+(4/(2^2)))" --> Ok 3Build an expression parser based on the given binding power and configuration.
This is the core function of the parser.
The expression function is actually implemented using
subExpression with a binding power of 0, like this:
expression : Config expr -> Parser expr
expression config =
subExpression 0 configsubExpression and expression are also used to make LED and NUD helpers.
For example prefix can be implemented like this:
prefix : Int -> Parser () -> (e -> e) -> Config expr -> Parser expr
prefix bindingPower operator apply config =
succeed apply
|. operator
|= subExpression bindingPower configOr a parser for sub-expressions between parentheses like this:
parens : Config Expr -> Parser Expr
parens config =
succeed identity
|. symbol "("
|= expression config
|. symbol ")"Or using prefix:
parens : Config Expr -> Parser Expr
parens config =
prefix 0 (symbol "(") identity config
|. symbol ")"Algorithm:
subExpression uses the following algorithm:
Use the Int binding power argument as the current binding power.
Run the spaces parser.
Try nuds parsers successively in order using
Parser.oneOf
until one is chosen (i.e. until one of them consumes characters).
This parser may call expression or subExpression recursively with
different binding power values.
If no parser succeeds, the whole parser fails, else the expression returned
by the nud parser is used as the left expression in the next steps.
Run the spaces parser.
Select leds parsers that have a binding power above the current one,
try them successively using Parser.oneOf with the left expression
argument until one is chosen. This parser may also call expression or
subExpressionrecursively with different binding power values.
If no led parser succeeds, return the left expression.
Else, loop from 4. using the expression just parsed by the led parser
as the new left expression.
Build a NUD parser for a constant.
The Config argument is passed automatically by the parser.
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Float -> Parser Float)
nuds =
[ constant (keyword "pi") pi ]
conf : Config Float
conf =
configure
{ nuds = nuds, leds = [], spaces = spaces }
run (expression conf) "pi" --> Ok piBuild a NUD parser for a prefix expression with a given binding power.
The Config argument is passed automatically by the parser.
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Int -> Parser Int)
nuds =
[ always int
, prefix 3 (symbol "-") negate
, prefix 3 (symbol "+") identity
]
conf : Config Int
conf =
configure
{ nuds = nuds, leds = [], spaces = spaces }
run (expression conf) "-1" --> Ok -1
run (expression conf) "--1" --> Ok 1
run (expression conf) "+1" --> Ok 1
run (expression conf) "+-+-1" --> Ok 1Build a LED parser for an infix expression with a left-associative operator and a given binding power.
The Config argument is passed automatically by the parser.
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Float -> Parser Float)
nuds =
[ always float ]
leds : List (Config Float -> (Int, Float -> Parser Float))
leds =
[ infixLeft 1 (symbol "+") (+)
, infixLeft 1 (symbol "-") (-)
, infixLeft 2 (symbol "*") (*)
, infixLeft 2 (symbol "/") (/)
]
conf : Config Float
conf =
configure
{ nuds = nuds, leds = leds, spaces = spaces }
run (expression conf) "5+4-3*2/1" --> Ok (5+4-(3*2/1))
run (expression conf) "5+4-3*2/1" --> Ok 3Build a LED parser for an infix expression with a right-associative operator and a given binding power.
The Config argument is passed automatically by the parser.
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Float -> Parser Float)
nuds =
[ always float ]
leds : List (Config Float -> (Int, Float -> Parser Float))
leds =
[ infixRight 4 (symbol "^") (^) ]
conf : Config Float
conf =
configure
{ nuds = nuds, leds = leds, spaces = spaces }
run (expression conf) "2^2^3" --> Ok (2^(2^3))
run (expression conf) "2^2^3" --> Ok 256Note: As usual in Pratt parsers, right-associativity is achieved by parsing the right expression with the binding power of the infix operator minus 1.
Build a LED parser for a postfix expression with a given binding power.
The Config argument is passed automatically by the parser.
import Parser exposing (..)
import Pratt exposing (..)
nuds : List (Config Float -> Parser Float)
nuds =
[ always float ]
leds : List (Config Float -> (Int, Float -> Parser Float))
leds =
[ postfix 6 (symbol "°") degrees ]
conf : Config Float
conf =
configure
{ nuds = nuds, leds = leds, spaces = spaces }
run (expression conf) "180°" --> Ok pi
run (expression conf) "360°" --> Ok (2*pi)
Build a parser based on the given configuration that can be combined with other
elm/parserparsers.The top level expression is parsed with a binding power of 0, see
subExpression.For example a parser that, given a
Config, requires the end of the string after having parsed successfully an expression, can be written like this:Let's test it with a simple integer
NUDparser:Our
spacesparser is set tosucceed ()and therefore does nothing, so trailing spaces are forbidden:Without
Parser.end, the parser will succeed, consuming only the first expression: