Packages

Money functions for operations on and localization of a money data type with support for ISO 4217 currencies and ISO 24165 digial tokens (crypto currencies).

Current section

Files

Jump to
ex_money lib money parser combinators.ex
Raw

lib/money/parser/combinators.ex

defmodule Money.Combinators do
@moduledoc false
import NimbleParsec
# Whitespace as defined by Unicode set :Zs plus tab
# non-breaking-space is considered a separator, not whitespace
@whitespace [?\s, ?\t, 0x1680, 0x2000, 0x202F, 0x205F, 0x3000]
def whitespace do
repeat(empty(), utf8_char(@whitespace))
|> label("whitespace")
end
@separators [?., ?,, , , ?、, ?︐, ?︑, ?﹐, ?﹑, ?,, ?、, ? , 0x00A0]
def separators do
utf8_char(@separators)
|> label("separators")
end
@decimal_places [?., ?․, ?。, ?︒, ?﹒, ?., ?。]
def decimal_place do
utf8_char(@decimal_places)
|> label("decimal place character")
end
@left_parens [?(]
def left_paren do
utf8_char(@left_parens)
|> label("left parenthesis")
end
@right_parens [?)]
def right_paren do
utf8_char(@right_parens)
|> label("right parenthesis")
end
@digits [?0..?9]
def digits do
repeat(empty(), ascii_char([?0..?9]))
|> label("digits")
end
@minus [?-]
def minus do
ascii_char(@minus)
end
def positive_number do
digits()
|> repeat(separators() |> concat(digits()))
|> optional(decimal_place() |> concat(digits()))
|> label("positive number")
end
def negative_number do
choice(empty(), [
ignore(minus())
|> concat(positive_number()),
positive_number()
|> ignore(minus())
])
|> reduce({List, :to_string, []})
|> map(:add_minus_sign)
|> label("negative number")
end
def number do
choice(empty(), [negative_number(), positive_number()])
|> reduce({List, :to_string, []})
|> unwrap_and_tag(:amount)
|> label("number")
end
@rtl [0x200F]
def rtl do
ignore(utf8_char(@rtl))
end
@invalid_chars @digits ++ @left_parens ++ @minus ++ @rtl
@currency Enum.map(@invalid_chars, fn s -> {:not, s} end)
def currency do
utf8_char(@currency)
|> times(min: 1)
|> reduce({List, :to_string, []})
|> unwrap_and_tag(:currency)
|> label("currency code, symbol or name")
end
def money_with_currency do
choice(empty(), [
optional(rtl())
|> concat(number())
|> ignore(optional(whitespace()))
|> optional(currency())
|> optional(rtl())
|> eos(),
optional(rtl())
|> optional(currency())
|> ignore(optional(whitespace()))
|> concat(number())
|> optional(rtl())
|> eos()
])
|> label("money with currency")
end
def accounting_format do
choice(empty(), [
ignore(left_paren())
|> ignore(optional(whitespace()))
|> concat(number())
|> ignore(optional(whitespace()))
|> optional(currency())
|> ignore(optional(whitespace()))
|> ignore(right_paren()),
ignore(left_paren())
|> ignore(optional(whitespace()))
|> optional(currency())
|> ignore(optional(whitespace()))
|> concat(number())
|> ignore(optional(whitespace()))
|> ignore(right_paren())
])
|> map(:change_sign)
|> label("money with currency in accounting format")
end
end