Packages
localize
1.0.0-rc.4
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
src/localize_plural_rules_parser.yrl
% Parse the CLDR Plural Rules definitions and return an Elixir AST
% Rules syntax and semantics are defined in
% http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
%
% condition = and_condition ('or' and_condition)*
% samples = ('@integer' sampleList)?
% ('@decimal' sampleList)?
% and_condition = relation ('and' relation)*
% relation = is_relation | in_relation | within_relation
% is_relation = expr 'is' ('not')? value
% in_relation = expr (('not')? 'in' | '=' | '!=') range_list
% within_relation = expr ('not')? 'within' range_list
% expr = operand (('mod' | '%') value)?
% operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
% range_list = (range | value) (',' range_list)*
% range = value'..'value
% sampleList = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
% sampleRange = decimalValue ('~' decimalValue)?
% value = digit+
% decimalValue = value ('.' value)?
% digit = 0|1|2|3|4|5|6|7|8|9
Nonterminals plural_rule condition and_condition relation is_relation in_relation within_relation
conditional expression range_list range_or_value range value samples sample_list sample_range.
Terminals mod is_op not_op sample operand tilde or_predicate and_predicate range_op
comma decimal integer ellipsis equals in within_op not_equals.
Rootsymbol plural_rule.
Right 100 mod.
Nonassoc 200 equals not_equals.
Left 300 and_predicate.
Left 400 or_predicate.
plural_rule -> condition samples : append({rule, '$1'}, '$2').
plural_rule -> condition : [{rule, '$1'}].
plural_rule -> samples : '$1'.
condition -> and_condition or_predicate condition : or_function('$1', '$3').
condition -> and_condition : '$1'.
and_condition -> relation and_predicate and_condition : and_function('$1', '$3').
and_condition -> relation : '$1'.
relation -> is_relation : '$1'.
relation -> in_relation : '$1'.
relation -> within_relation : '$1'.
is_relation -> expression is_op value : or_range_list('$1', '$3').
is_relation -> expression is_op not_op value : not_function(or_range_list('$1', '$4')).
in_relation -> expression not_equals range_list : not_function(or_range_list('$1', '$3')).
in_relation -> expression conditional range_list : or_range_list('$1', '$3').
in_relation -> expression not_op in range_list : not_function(or_range_list('$1', '$4')).
within_relation -> expression within_op range_list : or_range_list('$1', '$3').
within_relation -> expression not_op within_op range_list : not_function(or_range_list('$1', '$4')).
conditional -> in : 'in'.
conditional -> equals : '='.
% TODO It would be good to keep track of the operands used so we only generate those ones
% TODO We need to generate variables for the mod expressions to avoid duplicate calculations too
expression -> operand mod value : mod('$1', '$3').
expression -> operand : operand('$1').
range_list -> range_or_value comma range_list : append('$1', '$3').
range_list -> range_or_value : '$1'.
range_or_value -> range : '$1'.
range_or_value -> value : '$1'.
range -> value range_op value : range('$1', '$3').
value -> integer : unwrap('$1').
value -> decimal : unwrap('$1').
samples -> sample sample_list samples : append({unwrap('$1'), '$2'}, '$3').
samples -> sample sample_list : [{unwrap('$1'), '$2'}].
sample_list -> sample_range comma sample_list : append('$1', '$3').
sample_list -> sample_range : ['$1'].
sample_range -> value tilde value : range('$1', '$3').
sample_range -> value : '$1'.
sample_range -> ellipsis : 'ellipsis'.
Erlang code.
-export([kernel_context/0]).
% mod function will calculate the modulo in Java fashion
% for floats and decimals so that mod(4.3, 3) == 1.3
% The function itself is defined in Cldr.Math
mod(Operand, Value) ->
{'mod', kernel_context(), [operand(Operand), Value]}.
% Return a reference to an operand
operand(Operand) ->
{atomize(Operand), [], 'Elixir'}.
% 'and' function
and_function(A, B) ->
{'and', kernel_context(), [A, B]}.
% 'or' function
or_function(A, B) ->
{'or', kernel_context(), [A, B]}.
% 'not' function
not_function(A) ->
{'!', kernel_context(), [A]}.
% Range syntax
range(Start, End) ->
{'..', kernel_context(), [Start, End]}.
% Inclusion forms
% -> for a range. call a helper function `within`
% so we can handle integers and floats/decimals
% separately
conditional(equals, A, B = {'..', _C, [_From, _To]}) ->
{'within', kernel_context(), [A, B]};
% -> for an expression
% NOTE this will calculate the expression each time which is
% inefficient. But parser isn't the right place to unwrap that.
% NOTE REMOVED DUE TO DIALYZER ERROR saying this can never match.
% tests still pass without it.
% conditional({'mod', [_C], [{_L}]}, A, B) ->
% {'==', kernel_context(), [A, B]};
% -> for a value
conditional(equals, A, B) ->
{'==', kernel_context(), [A, B]}.
% Convert a range list into a postfix 'or' form
% Just two items in the list
or_range_list(Operand, [A, B]) ->
or_function(conditional(equals, Operand, A),
conditional(equals, Operand, B));
% Many items in a list
or_range_list(Operand, [A | B]) ->
or_function(conditional(equals, Operand, A),
or_range_list(Operand, B));
% When there's only one value
or_range_list(Operand, Value) ->
conditional(equals, Operand, Value).
% Append to build up a list of ranges or values
append(A, B) when is_list(A) and is_list(B) ->
A ++ B;
append(A, B) when is_list(A) and not is_list(B)->
A ++ [B];
append(A, B) when not is_list(A) and is_list(B) ->
[A] ++ B;
append(A, B) when not is_list(A) and not is_list(B) ->
[A, B].
% Return a token value
unwrap({_,_,V}) -> V.
% Elixir Kernel Context
kernel_context() ->
[{context, 'Elixir'}, {import, 'Elixir.Kernel'}].
% Atomize a token value
atomize(Token) ->
list_to_atom(unwrap(Token)).