elm-tools/parser/2.0.1

NOTE: this package is not compatible with Elm 0.19.1

Parser.LanguageKit

Variables

variable : (Char -> Bool) -> (Char -> Bool) -> Set String -> Parser String

Create a parser for variables. It takes two Char checkers. The first one is for the first character. The second one is for all the other characters.

In Elm, we distinguish between upper and lower case variables, so we can do something like this:

import Char
import Parser exposing (..)
import Parser.LanguageKit exposing (variable)
import Set


lowVar : Parser String
lowVar =
  variable Char.isLower isVarChar keywords


capVar : Parser String
capVar =
  variable Char.isUpper isVarChar keywords


isVarChar : Char -> Bool
isVarChar char =
  Char.isLower char
  || Char.isUpper char
  || Char.isDigit char
  || char == '_'


keywords : Set.Set String
keywords =
  Set.fromList [ "let", "in", "case", "of" ]

Lists, records, and that sort of thing

list : Parser () -> Parser a -> Parser (List a)

Parse a comma-separated list like [ 1, 2, 3 ]. You provide a parser for the spaces and for the list items. So if you want to parse a list of integers, you would say:

import Parser exposing (Parser)
import Parser.LanguageKit as Parser


intList : Parser (List Int)
intList =
  Parser.list spaces Parser.int


spaces : Parser ()
spaces =
  Parser.ignore zeroOrMore (\char -> char == ' ')


-- run intList "[]"            == Ok []
-- run intList "[ ]"           == Ok []
-- run intList "[1,2,3]"       == Ok [1,2,3]
-- run intList "[ 1, 2, 3 ]"   == Ok [1,2,3]
-- run intList "[ 1 , 2 , 3 ]" == Ok [1,2,3]
-- run intList "[ 1, 2, 3, ]"  == Err ...
-- run intList "[, 1, 2, 3 ]"  == Err ...

Note: If you want trailing commas, check out the sequence function.

record : Parser () -> Parser a -> Parser (List a)

Help parse records like { a = 2, b = 2 }. You provide a parser for the spaces and for the list items, you might say:

import Parser exposing ( Parser, (|.), (|=), zeroOrMore )
import Parser.LanguageKit as Parser


record : Parser (List (String, Int))
record =
  Parser.record spaces field


field : Parser (String, Int)
field =
  Parser.succeed (,)
    |= lowVar
    |. spaces
    |. Parser.symbol "="
    |. spaces
    |= int


spaces : Parser ()
spaces =
  Parser.ignore zeroOrMore (\char -> char == ' ')


-- run record "{}"               == Ok []
-- run record "{ }"              == Ok []
-- run record "{ x = 3 }"        == Ok [ ("x",3) ]
-- run record "{ x = 3, }"       == Err ...
-- run record "{ x = 3, y = 4 }" == Ok [ ("x",3), ("y",4) ]
-- run record "{ x = 3, y = }"   == Err ...

Note: If you want trailing commas, check out the sequence function.

tuple : Parser () -> Parser a -> Parser (List a)

Help parse tuples like (3, 4). Works just like list and record. And if you need something custom, check out the sequence function.

{ start : String
, separator : String
, end : String
, spaces : Parser ()
, item : Parser a
, trailing : Trailing
}
-> Parser (List a)

Handle things like lists and records, but you can customize the details however you need. Say you want to parse C-style code blocks:

import Parser exposing (Parser)
import Parser.LanguageKit as Parser exposing (Trailing(..))


block : Parser (List Stmt)
block =
  Parser.sequence
    { start = "{"
    , separator = ";"
    , end = "}"
    , spaces = spaces
    , item = statement
    , trailing = Mandatory -- demand a trailing semi-colon
    }


-- spaces : Parser ()
-- statement : Parser Stmt

Note: If you need something more custom, do not be afraid to check out the implementation and customize it for your case. It is better to get nice error messages with a lower-level implementation than to try to hack high-level parsers to do things they are not made for.

= Forbidden
| Optional
| Mandatory

What’s the deal with trailing commas? Are they Forbidden? Are they Optional? Are they Mandatory? Welcome to shapes club!

Whitespace

{ allowTabs : Bool
, lineComment : LineComment
, multiComment : MultiComment
}
-> Parser ()

Create a custom whitespace parser. It will always chomp the ' ', '\r', and '\n' characters, but you can customize some other things. Here are some examples:

elm : Parser ()
elm =
  whitespace
    { allowTabs = False
    , lineComment = LineComment "--"
    , multiComment = NestableComment "{-" "-}"
    }


js : Parser ()
js =
  whitespace
    { allowTabs = True
    , lineComment = LineComment "//"
    , multiComment = UnnestableComment "/*" "*/"
    }

If you need further customization, please open an issue describing your scenario or check out the source code and write it yourself. This is all built using stuff from the root Parser module.

= NoLineComment
| LineComment String

Are line comments allowed? If so, what symbol do they start with?

LineComment "--"   -- Elm
LineComment "//"   -- JS
LineComment "#"    -- Python
NoLineComment      -- OCaml
= NoMultiComment
| NestableComment String String
| UnnestableComment String String

Are multi-line comments allowed? If so, what symbols do they start and end with?

NestableComment "{-" "-}"    -- Elm
UnnestableComment "/*" "*/"  -- JS
NoMultiComment               -- Python

In Elm, you can nest multi-line comments. In C-like languages, like JS, this is not allowed. As soon as you see a */ the comment is over no matter what.