Current section

Files

Jump to
ex_cldr_units lib cldr unit format.ex
Raw

lib/cldr/unit/format.ex

defmodule Cldr.Unit.Format do
alias Cldr.Unit
@typep grammar ::
{Unit.translatable_unit(),
{Unit.grammatical_case(), Cldr.Number.PluralRule.plural_type()}}
@typep grammar_list :: [grammar, ...]
@doc """
Traverses the components of a unit
and resolves a list of base units with
their gramatical case and plural selector
definitions for a given locale.
This function relies upon the internal
representation of units and grammatical features
and is primarily for the support of
formatting a function through `Cldr.Unit.to_string/2`.
## Arguments
* `unit` is a `t:Cldr.Unit` or a binary
unit string
## Options
* `:locale` is any valid locale name returned by `Cldr.known_locale_names/1`
or a `t:Cldr.LanguageTag` struct. The default is `Cldr.get_locale/0`
* `backend` is any module that includes `use Cldr` and therefore
is a `Cldr` backend module. The default is `Cldr.default_backend!/0`.
## Returns
## Examples
"""
@doc since: "3.5.0"
@spec grammar(Unit.t(), Keyword.t()) :: grammar_list() | {grammar_list(), grammar_list()}
def grammar(unit, options \\ [])
def grammar(%Unit{} = unit, options) do
{locale, backend} = Cldr.locale_and_backend_from(options)
module = Module.concat(backend, :Unit)
features =
module.grammatical_features("root")
|> Map.merge(module.grammatical_features(locale))
gcase = Map.fetch!(features, :case)
plural = Map.fetch!(features, :plural)
traverse(unit, &grammar(&1, gcase, plural, options))
end
def grammar(unit, options) when is_binary(unit) do
grammar(Unit.new!(1, unit), options)
end
defp grammar({:unit, unit}, _gcase, _plural, _options) do
{unit, {:compound, :compound}}
end
defp grammar({:per, {left, right}}, _gcase, _plural, _options)
when is_list(left) and is_list(right) do
{left, right}
end
defp grammar({:per, {left, {right, _}}}, gcase, plural, _options) when is_list(left) do
{left, [{right, {gcase.per[1], plural.per[1]}}]}
end
defp grammar({:per, {{left, _}, right}}, gcase, plural, _options) when is_list(right) do
{[{left, {gcase.per[0], plural.per[0]}}], right}
end
defp grammar({:per, {{left, _}, {right, _}}}, gcase, plural, _options) do
{[{left, {gcase.per[0], plural.per[0]}}], [{right, {gcase.per[1], plural.per[1]}}]}
end
defp grammar({:times, {left, right}}, _gcase, _plural, _options)
when is_list(left) and is_list(right) do
left ++ right
end
defp grammar({:times, {{left, _}, right}}, gcase, plural, _options) when is_list(right) do
[{left, {gcase.times[0], plural.times[0]}} | right]
end
defp grammar({:times, {left, {right, _}}}, gcase, plural, _options) when is_list(left) do
left ++ [{right, {gcase.times[1], plural.times[1]}}]
end
defp grammar({:times, {{left, _}, {right, _}}}, gcase, plural, _options) do
[{left, {gcase.times[0], plural.times[0]}}, {right, {gcase.times[1], plural.times[1]}}]
end
defp grammar({:power, {{left, _}, right}}, gcase, plural, _options) when is_list(right) do
[{left, {gcase.power[0], plural.power[0]}} | right]
end
defp grammar({:power, {{left, _}, {right, _}}}, gcase, plural, _options) do
[{left, {gcase.power[0], plural.power[0]}}, {right, {gcase.power[1], plural.power[1]}}]
end
defp grammar({:prefix, {{left, _}, {right, _}}}, gcase, plural, _options) do
[{left, {gcase.prefix[0], plural.prefix[0]}}, {right, {gcase.prefix[1], plural.prefix[1]}}]
end
@doc """
Traverses a unit's decomposition and invokes
a function on each node of the composition
tree.
## Arguments
* `unit` is any unit returned by `Cldr.Unit.new/2`
* `fun` is any single-arity function. It will be invoked
for each node of the composition tree. The argument is a tuple
of the following form:
* `{:unit, argument}`
* `{:times, {argument_1, argument_2}}`
* `{:prefix, {prefix_unit, argument}}`
* `{:power, {power_unit, argument}}`
* `{:per, {argument_1, argument_2}}`
Where the arguments are the results returned
from the `fun/1`.
## Returns
The result returned from `fun/1`
"""
def traverse(%Unit{base_conversion: {left, right}}, fun) when is_function(fun) do
fun.({:per, {do_traverse(left, fun), do_traverse(right, fun)}})
end
def traverse(%Unit{base_conversion: conversion}, fun) when is_function(fun) do
do_traverse(conversion, fun)
end
defp do_traverse([{unit, _}], fun) do
do_traverse(unit, fun)
end
defp do_traverse([head | rest], fun) do
fun.({:times, {do_traverse(head, fun), do_traverse(rest, fun)}})
end
defp do_traverse({unit, _}, fun) do
do_traverse(unit, fun)
end
@si_prefix Cldr.Unit.Prefix.si_power_prefixes()
@power Cldr.Unit.Prefix.power_units() |> Map.new()
# String decomposition
for {power, exp} <- @power do
power_unit = String.to_atom("power#{exp}")
defp do_traverse(unquote(power) <> "_" <> unit, fun) do
fun.({:power, {fun.({:unit, unquote(power_unit)}), do_traverse(unit, fun)}})
end
end
for {prefix, exp} <- @si_prefix do
prefix_unit = String.to_atom("10p#{exp}" |> String.replace("-", "_"))
defp do_traverse(unquote(prefix) <> unit, fun) do
fun.(
{:prefix,
{fun.({:unit, unquote(prefix_unit)}), fun.({:unit, String.to_existing_atom(unit)})}}
)
end
end
defp do_traverse(unit, fun) when is_binary(unit) do
fun.({:unit, String.to_existing_atom(unit)})
end
defp do_traverse(unit, fun) when is_atom(unit) do
fun.({:unit, unit})
end
end