pete-murphy/elm-nlds/3.0.1

Nld

Natural Language Disambiguator - a flexible parser for loosely ordered token sequences.

Nld parses sequences of tokens while:

  • Ignoring irrelevant tokens
  • Allowing tokens to appear in any order (preferring specified order)
  • Lazily producing results in priority order

Unlike traditional parser combinators, Nld is robust to reordering and alternate phrasings, making it useful for parsing natural language input.

Running Parsers

type Nld a

An Nld a is a parser that produces values of type a from a sequence of tokens. It explores multiple parse branches lazily, preferring matches where tokens appear in the specified order and closer together.

run : Nld a -> TokenPositions -> List ( Float, a )

Run a parser on TokenPositions and return all results.

runList : Nld a -> List String -> List a

Run a parser on a list of tokens and return all results. Results are ordered by priority (best matches first).

runList (word "hello") [ "hello", "world" ]
--> [ "hello" ]


runList (tuple2 (word "delete") (word "file")) [ "file", "delete" ]
--> [ ( "delete", "file" ) ]
runTake : Int -> Nld a -> List String -> List a

Run a parser and take only the first n results.

runTake 1 (word "cat") [ "the", "cat", "sat" ]
--> [ "cat" ]

Token Matchers

word : String -> Nld String

Match a specific word/token.

runList (word "delete") [ "please", "delete", "this" ]
--> [ "delete" ]
words : List String -> Nld String

Match any of the given words, preferring earlier ones in the list. Useful for synonyms.

runList (words [ "delete", "remove", "erase" ]) [ "please", "erase", "this" ]
--> [ "delete" ]

The result is canonicalized to the first word in the list.

token : Nld String

Match any token and return it.

runTake 1 token [ "anything" ]
--> [ "anything" ]
nat : Nld Int

Match a natural number (non-negative integer).

runList (tuple2 (word "buy") nat) [ "buy", "3", "apples" ]
--> [ ( "buy", 3 ) ]
int : Nld Int

Match an integer (positive or negative).

runList (tuple2 (word "offset") int) [ "offset", "-5", "pixels" ]
--> [ ( "offset", -5 ) ]
float : Nld Float

Match a floating-point number.

runList float [ "3.14" ]
--> [ 3.14 ]


runList float [ "set", "rate", "to", "2.5" ]
--> [ 2.5 ]
boolean : Nld Bool

Match a boolean token. Accepts case-insensitive variations: "true", "True", "TRUE", "false", "False", "FALSE", etc.

runList boolean [ "true" ]
--> [ True ]


runList boolean [ "set", "verbose", "True" ]
--> [ True ]
tokenMatching : (String -> Bool) -> Nld String

Match tokens satisfying a predicate.

runList (tokenMatching (String.endsWith ".txt")) [ "open", "report.txt" ]
--> [ "report.txt" ]
minimalToken : List ( Float, String ) -> Nld String

Match any of several weighted tokens. Lower weights are preferred.

runTake 1 (minimalToken [ ( 0.0, "delete" ), ( 1.0, "remove" ) ]) [ "remove", "delete" ]
--> [ "delete" ]

Indexed Token Matchers

These return both the matched value and its position in the input.

indexedWord : String -> Nld ( String, Int )

Match a specific word and return both the word and its position.

runList (indexedWord "cat") [ "the", "cat", "sat" ]
--> [ ( "cat", 1 ) ]
indexedWords : List String -> Nld ( String, Int )

Match any of the given words and return the canonical (first) word with its position.

Note: Like words, this canonicalizes the result to the first word in the list. For example, indexedWords ["hello", "hi"] with input ["hi"] returns [("hello", 0)].

indexedToken : Nld ( String, Int )

Match any token and return it with its position.

indexedNat : Nld ( Int, Int )

Match a natural number and return it with its position.

indexedInt : Nld ( Int, Int )

Match an integer (positive or negative) and return it with its position.

indexedFloat : Nld ( Float, Int )

Match a floating-point number and return it with its position.

indexedBoolean : Nld ( Bool, Int )

Match a boolean token and return it with its position. Accepts case-insensitive variations: "true", "True", "TRUE", "false", "False", "FALSE", etc.

indexedTokenMatching : (String -> Bool) -> Nld ( String, Int )

Match tokens satisfying a predicate and return with position.

indexedTokenOfType : TokenType -> (String -> Maybe a) -> Nld ( a, Int )

Match a token of a given TokenType using a custom parse function. The TokenType is used in the Grammar representation.

This is used to define indexedNat, indexedFloat, etc.

indexedMinimalToken : List ( Float, String ) -> Nld ( String, Int )

Match any of several weighted tokens, returning both the token and its position. Lower weights are preferred.

Transforming and Combining

succeed : a -> Nld a

A parser that always succeeds with the given value without consuming any tokens.

This is the fundamental building block for applicative-style parsing with andMap.

runList (succeed 42) [ "any", "tokens" ]
--> [ 42 ]
map : (a -> b) -> Nld a -> Nld b

Transform the result of a parser.

runList (map String.toUpper (word "hello")) [ "hello" ]
--> [ "HELLO" ]
map2 : (a -> b -> c) -> Nld a -> Nld b -> Nld c

Combine two parsers.

map3 : (a -> b -> c -> d) -> Nld a -> Nld b -> Nld c -> Nld d

Combine three parsers.

map4 : (a -> b -> c -> d -> e) -> Nld a -> Nld b -> Nld c -> Nld d -> Nld e

Combine four parsers.

map5 : (a -> b -> c -> d -> e -> f) -> Nld a -> Nld b -> Nld c -> Nld d -> Nld e -> Nld f

Combine five parsers.

(a -> b -> c -> d -> e -> f -> g)
-> Nld a
-> Nld b
-> Nld c
-> Nld d
-> Nld e
-> Nld f
-> Nld g

Combine six parsers.

(a -> b -> c -> d -> e -> f -> g -> h)
-> Nld a
-> Nld b
-> Nld c
-> Nld d
-> Nld e
-> Nld f
-> Nld g
-> Nld h

Combine seven parsers.

(a -> b -> c -> d -> e -> f -> g -> h -> i)
-> Nld a
-> Nld b
-> Nld c
-> Nld d
-> Nld e
-> Nld f
-> Nld g
-> Nld h
-> Nld i

Combine eight parsers.

andThen : (a -> Nld b) -> Nld a -> Nld b

Sequence two parsers. The second parser can depend on the result of the first.

andMap : Nld a -> Nld (a -> b) -> Nld b

Apply a parser producing a function to a parser producing a value.

This enables pipe-style composition for building parsers with arbitrary arity:

runList
    (succeed Tuple.pair
        |> andMap (word "buy")
        |> andMap nat
    )
    [ "buy", "3" ]
--> [ ( "buy", 3 ) ]
tuple2 : Nld a -> Nld b -> Nld ( a, b )

Combine two parsers into a tuple.

runList (tuple2 (word "delete") (word "file")) [ "delete", "file" ]
--> [ ( "delete", "file" ) ]
tuple3 : Nld a -> Nld b -> Nld c -> Nld ( a, b, c )

Combine three parsers into a tuple.

Alternatives, Repetition, and Sequencing

choice : List (Nld a) -> Nld a

Try multiple parsers and return all successful parses.

repeat : Nld a -> Nld (List a)

Match zero or more occurrences of a parser.

Returns all prefixes of the greedy (in-order) match:

  • For repeat nat with ["1", "2", "3"], returns: [[1, 2, 3], [1, 2], [1], []]

The greedy collection always takes elements in position order.

combine : List (Nld a) -> Nld (List a)

Run each parser in the list and collect the results into a list.

runList (combine [ word "a", word "b" ]) [ "a", "b" ]
--> [ [ "a", "b" ] ]

Individual elements can use words for synonyms:

runList (combine [ words [ "remove", "delete" ], words [ "file", "document" ] ]) [ "please", "erase", "the", "document" ]
--> []
phrase : List String -> Nld String

Match all of the words in the list (using word for each) and return them joined by spaces.

runList (phrase [ "delete", "file" ]) [ "please", "delete", "the", "file" ]
--> [ "delete file" ]


runList (tuple2 (phrase [ "set", "volume" ]) nat) [ "set", "the", "volume", "to", "11" ]
--> [ ( "set volume", 11 ) ]

Autocompletion

autocomplete : Nld a -> TokenPositions -> Peach (Set String)

Get autocomplete suggestions as a Peach.

This walks the parse tree, collecting suggestions at every point where the parser fails due to missing tokens. The result is a lazy stream of suggestion sets, ordered by parse weight.

topK : Int -> Nld a -> List String -> List (Set String)

Get autocomplete suggestions for incomplete input. Returns a list of sets of tokens that could complete the parse.

import Set


topK 5 (word "buy") []
-- Returns [ Set.fromList [ "buy" ] ]


topK 5 (tuple2 (word "buy") (word "apples")) [ "buy" ]
-- Returns [ Set.fromList [ "apples" ] ]

This explores the parse tree lazily, finding all points where the parser needs tokens that aren't present in the input.

Grammar Introspection

Extract the Grammar from an Nld parser. The Grammar is a first-class description of what the parser expects, useful for introspection and serialization.

import Nld.Grammar exposing (Grammar(..))
import Nld.TokenType exposing (TokenType(..))


grammar (word "hello")
--> Literal "hello"


grammar (tuple2 (word "buy") nat)
--> Seq [ Literal "buy", Token NatToken ]
wanted : Nld a -> Set String

Returns the set of tokens that the parser is looking for. This is used internally for optimization but can be useful for debugging.

import Set


wanted (word "hello")
--> Set.fromList [ "hello" ]

Tokenizing

tokenize : String -> List String

Split a text string into tokens suitable for use with runList, runTake, and other Nld runners. It handles:

  • Whitespace: splits on whitespace, discarding it.
  • camelCase: splits at lowercase-to-uppercase boundaries.
  • Uppercase runs: keeps runs of uppercase letters together, splitting before the final uppercase letter when followed by lowercase (e.g. "HTML" and "Parser").
  • Alpha/numeric boundaries: splits between letters and digits.
  • Punctuation: each non-alphanumeric, non-whitespace character becomes its own token.

Examples:

tokenize "hello world"
--> [ "hello", "world" ]


tokenize "camelCase"
--> [ "camel", "Case" ]


tokenize "HTMLParser"
--> [ "HTML", "Parser" ]


tokenize "abcra123"
--> [ "abcra", "123" ]


tokenize "(hi!)"
--> [ "(", "hi", "!", ")" ]


tokenize "setHTMLParser"
--> [ "set", "HTML", "Parser" ]