davidcavazos/parser/6.1.1

NOTE: the latest version is 7.0.0

Renamed to lambda-phi/parser. It is published under that name, and new versions come out there.

Versions published as davidcavazos/parser still install under that name. 2 versions of 12 come from the new location (see their notes). 10 versions could not be recovered and cannot be installed.

Rerouted (cache, verified). The original is gone. This copy comes from a Lamdera package cache made before it went. The Lamdera package server serves a copy recovered from a Lamdera package cache from 2021-02-13.

What we checked: Rebuilt from the unpacked sources in a Lamdera package cache (5 copies agree), which Elm only writes after the published SHA-1 checked out; elm.json is byte-identical to package.elm-lang.org's; the exposed modules match docs.json; the zip is our own, so its SHA-1 is not the published one.

The Lamdera compiler uses this copy and prints a one-line notice saying so. elm itself fails to download this version.

Details · package.elm-lang.org's zip: https://github.com/davidcavazos/parser/zipball/6.1.1/

Parser.Expression

Parsers for expressions with operator precedence.

Basic usage

expression : List (List (Operator a)) -> Parser a

Parses an expression using the provided operators with operator precedence. There can be multiple operators sharing the same precedence like + and -, or * and /.

import Parser exposing (Parser, andThen, drop, parse, succeed, take)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


factorial : Float -> Float
factorial n =
    List.product (List.range 1 (floor n) |> List.map toFloat)


calculate : Parser Float
calculate =
    expression
        [ [ prefix (char '+') identity
          , prefix (char '-') (\x -> -x)
          , suffix (char '!') factorial
          ]
        , [ fromRight (char '^') (^)
          ]
        , [ fromLeft (char '*') (*)
          , fromLeft (char '/') (/)
          ]
        , [ fromLeft (char '+') (+)
          , fromLeft (char '-') (-)
          ]
        , [ inbetween (char '(') (char ')') identity
          , term number
          ]
        ]


-- Terms and parentheses.
parse "1" calculate   --> Ok 1
parse "(1)" calculate --> Ok 1


-- Prefix operators.
parse "-1" calculate  --> Ok -1  --   -1
parse "+-1" calculate --> Ok -1  -- +(-1)
parse "-+1" calculate --> Ok -1  -- -(+1)
parse "--1" calculate --> Ok 1   -- -(-1)


-- Suffix operators.
parse "5!" calculate  --> Ok 120  -- 5!
parse "3!!" calculate --> Ok 720  -- (3!)!


-- Left-associative binary operators.
parse "1+2+3" calculate   --> Ok 6    -- (1 + 2) + 3
parse "1-2-3" calculate   --> Ok -4   -- (1 - 2) - 3
parse "1-(2-3)" calculate --> Ok 2    -- 1 - (2 - 3)
parse "2*3*4" calculate   --> Ok 24   -- (2 * 3) * 4
parse "2/4/5" calculate   --> Ok 0.1  -- (2 / 4) / 5
parse "2/(4/5)" calculate --> Ok 2.5  -- 2 / (4 / 5)


-- Right-associative binary operators.
parse "2^3^2" calculate   --> Ok 512  -- 2 ^ (3 ^ 2)
parse "(2^3)^2" calculate --> Ok 64   -- (2 ^ 3) ^ 2)


-- Operator precedence.
parse "1+-2" calculate  --> Ok -1  -- 1 + (-2)
parse "1+2*3" calculate --> Ok 7   -- 1 + (2 * 3)
parse "1*2+3" calculate --> Ok 5   -- (1 * 2) + 3
parse "1*2^3" calculate --> Ok 8   -- 1 * (2 ^ 3)
parse "1^2*3" calculate --> Ok 3   -- (1 ^ 2) * 3

Operators

term : Parser a -> Operator a

Defines an individual term. This is usually a number, or a variable name, or anything not containing an operator.

import Parser exposing (Parser, parse)
import Parser.Common exposing (number)


expr : Parser Float
expr =
    expression [ [ term number ] ]


parse "5" expr --> Ok 5


-- It does not trim spaces beforehand.
parse " 5" expr |> Result.toMaybe --> Nothing
prefix : Parser op -> (a -> a) -> Operator a

Defines a unary prefix operator.

import Parser exposing (Parser, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


expr : Parser Float
expr =
    expression
        [ [ prefix (char '-') (\x -> -x) ]
        , [ term number ]
        ]


parse "-5" expr --> Ok -5


-- It does not trim spaces beforehand.
parse " -5" expr |> Result.toMaybe --> Nothing


-- But it does afterwards.
parse "- 5" expr --> Ok -5
suffix : Parser op -> (a -> a) -> Operator a

Defines a unary suffix operator.

import Parser exposing (Parser, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


factorial : Float -> Float
factorial n =
    List.product (List.range 1 (floor n) |> List.map toFloat)


expr : Parser Float
expr =
    expression
        [ [ suffix (char '!') factorial ]
        , [ term number ]
        ]


parse "5!" expr --> Ok 120


-- It trims spaces beforehand.
parse "5 !" expr --> Ok 120
inbetween : Parser open -> Parser close -> (a -> a) -> Operator a

Defines an expression surrounded by an open and close operator.

import Parser exposing (Parser, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


expr : Parser Float
expr =
    expression
        [ [ inbetween (char '(') (char ')') identity ]
        , [ term number ]
        ]


parse "(5)" expr --> Ok 5
parse "(5" expr |> Result.toMaybe --> Nothing
parse "()" expr |> Result.toMaybe --> Nothing


-- It does not trim spaces beforehand.
parse " (5)" expr |> Result.toMaybe --> Nothing


-- But it does inbetween.
parse "( 5 )" expr --> Ok 5
fromLeft : Parser op -> (a -> a -> a) -> Operator a

Defines a binary left-associative operator.

import Parser exposing (Parser, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


expr : Parser Float
expr =
    expression
        [ [ fromLeft (char '+') (+)
          , fromLeft (char '-') (-)
          ]
        , [ term number ]
        ]


parse "1+2" expr   --> Ok 3
parse "1+2+3" expr --> Ok 6
parse "1-2-3" expr --> Ok -4


-- It does not trim spaces beforehand.
parse " 1+2" expr |> Result.toMaybe --> Nothing


-- But it does inbetween.
parse "1 + 2" expr --> Ok 3
fromRight : Parser op -> (a -> a -> a) -> Operator a

Defines a binary right-associative operator.

import Parser exposing (Parser, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


expr : Parser Float
expr =
    expression
        [ [ fromRight (char '^') (^) ]
        , [ term number ]
        ]


parse "2^3" expr   --> Ok 8
parse "2^3^2" expr --> Ok 512 -- 2 ^ (3 ^ 2)


-- It does not trim spaces beforehand.
parse " 2^3" expr |> Result.toMaybe --> Nothing


-- But it does inbetween.
parse "2 ^ 3" expr --> Ok 8

Custom operators

type Operator a
= Prefix (Parser a -> Parser a)
| InfixFromLeft (Parser a -> Parser (a -> a))
| InfixFromRight (Parser a -> Parser (a -> a))

Describes an operator as a parser. They take a Parser a as an input, which is used to parse subexpressions recursively.

ℹ️ Both Prefix and Infix* operators must start by parsing something other than a subexpression, otherwise there will be a stack overflow.

import Parser exposing (Parser, drop, parse, succeed, take)
import Parser.Char exposing (char)
import Parser.Common exposing (number)


-- Prefix operators simply parse and apply the operation.
neg : Operator Float
neg =
    Prefix
        (\expr ->
            succeed (\x -> -x)
                |> drop (char '-')
                |> take expr
        )


-- Infix operators parse only the right side of the subexpression.
-- The Parser returns a function that takes the left side of the
-- subexpression as an input, and applies the operation.
-- This is for a left-associative infix operator.
add : Operator Float
add =
    InfixFromLeft
        (\expr ->
            succeed (\right left -> left + right)
                |> drop (char '+')
                |> take expr
        )


-- You can also define a right-associative infix operator.
pow : Operator Float
pow =
    InfixFromLeft
        (\expr ->
            succeed (\right left -> left ^ right)
                |> drop (char '^')
                |> take expr
        )


-- A term can be achieved by simply parsing a token but not recursing.
num : Operator Float
num =
    Prefix (\_ -> number)


calculate : Parser Float
calculate =
    expression
        [ [ neg ] -- -1
        , [ pow ] -- 1 ^ 2
        , [ add ] -- 1 + 2
        , [ num ] -- 1
        ]


parse "1+-2^3" calculate --> Ok -7 -- 1 + ((-2) ^ 3)