a `BS @sdZddlZddlmZdZdZddZGdd d eZd d Z d d Z ddZ ddZ ddZ ddZGdddeZdZdedejfdedefdedfdedfd ed!ejfgZd"d#ZdBd$d%ZdCd&d'Zd(d)Zd*d+Zd,d-Zd.d/ZGd0d1d1eZd2d3Zd4d5Zd6d7Z Gd8d9d9eZ!Gd:d;d;e!Z"Gdd?d?e#Z$Gd@dAdAe!Z%dS)Dz babel.numbers ~~~~~~~~~~~~~ CLDR Plural support. See UTS #35. :copyright: (c) 2013-2021 by the Babel Team. :license: BSD, see LICENSE for more details. N)decimal)ZzeroZoneZtwofewmanyotherrc Cst|}t|}t|tr6||kr(|}ntt|}t|tjr|}|j}|dkrf|j |dnd}d dd|D}| d}t |}t |} t|pd} t|pd} nd}} } } |||| | | fS)a#Extract operands from a decimal, a float or an int, according to `CLDR rules`_. The result is a 6-tuple (n, i, v, w, f, t), where those symbols are as follows: ====== =============================================================== Symbol Value ------ --------------------------------------------------------------- n absolute value of the source number (integer and decimals). i integer digits of n. v number of visible fraction digits in n, with trailing zeros. w number of visible fraction digits in n, without trailing zeros. f visible fractional digits in n, with trailing zeros. t visible fractional digits in n, without trailing zeros. ====== =============================================================== .. _`CLDR rules`: https://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Operands :param source: A real number :type source: int|float|decimal.Decimal :return: A n-i-v-w-f-t tuple :rtype: tuple[decimal.Decimal, int, int, int, int, int] rNcss|]}t|VqdSNstr).0drr0/usr/lib/python3.9/site-packages/babel/plural.py Az#extract_operands..0) absint isinstancefloatrZDecimalr Zas_tupleZexponentdigitsjoinrstriplen) sourceniZ dec_tupleZexpZfraction_digitsZtrailingZ no_trailingvwftrrr extract_operandss$     r c@sdeZdZdZdZddZddZeddZe d d Z e d d d dZ ddZ ddZ ddZdS) PluralRuleafRepresents a set of language pluralization rules. The constructor accepts a list of (tag, expr) tuples or a dict of `CLDR rules`_. The resulting object is callable and accepts one parameter with a positive or negative number (both integer and float) for the number that indicates the plural form for a string and returns the tag for the format: >>> rule = PluralRule({'one': 'n is 1'}) >>> rule(1) 'one' >>> rule(2) 'other' Currently the CLDR defines these tags: zero, one, two, few, many and other where other is an implicit default. Rules should be mutually exclusive; for a given numeric value, only one rule should apply (i.e. the condition should only be true for one of the plural rule elements. .. _`CLDR rules`: https://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Language_Plural_Rules )abstract_funccCst|tr|}t}g|_tt|D]Z\}}|tvrHtd|n||vr\td|| |t |j }|r*|j ||fq*dS)a$Initialize the rule instance. :param rules: a list of ``(tag, expr)``) tuples with the rules conforming to UTS #35 or a dict with the tags as keys and expressions as values. :raise RuleError: if the expression is malformed zunknown tag %rztag %r defined twiceN) rdictitemssetr"sortedlist _plural_tags ValueErroradd_Parserastappend)selfrulesfoundkeyexprr-rrr __init__cs    zPluralRule.__init__cs,|jdt|jdfddtDfS)Nz<%s %r>z, cs$g|]}|vrd||fqS)z%s: %srr tagr0rr }sz'PluralRule.__repr__..)r0type__name__rr)r/rr7r __repr__ys zPluralRule.__repr__cCst||r|S||S)a Create a `PluralRule` instance for the given rules. If the rules are a `PluralRule` object, that object is returned. :param rules: the rules as list or dict, or a `PluralRule` object :raise RuleError: if the expression is malformed )r)clsr0rrr parses zPluralRule.parsecs tjtfdd|jDS)zThe `PluralRule` as a dict of unicode plural rules. >>> rule = PluralRule({'one': 'n is 1'}) >>> rule.rules {'one': 'n is 1'} csg|]\}}||fqSrr)r r6r-_compilerr r8rz$PluralRule.rules..)_UnicodeCompilercompiler$r"r;rr?r r0szPluralRule.rulescCstdd|jDS)NcSsg|] }|dqS)rr)r rrrr r8rz'PluralRule...) frozensetr"xrrr rzPluralRule.z A set of explicitly defined tags in this rule. The implicit default ``'other'`` rules is not part of this set unless there is an explicit rule for it.)doccCs|jSrr"r;rrr __getstate__szPluralRule.__getstate__cCs ||_dSrrH)r/r"rrr __setstate__szPluralRule.__setstate__cCst|dst||_||S)Nr#)hasattr to_pythonr#)r/rrrr __call__s  zPluralRule.__call__N)r: __module__ __qualname____doc__ __slots__r4r< classmethodr>propertyr0tagsrIrJrMrrrr r!Ls  r!cCsRtj}dg}t|jD]\}}|d|||fq|dtd|S)aConvert a list/dict of rules or a `PluralRule` object into a JavaScript function. This function depends on no external library: >>> to_javascript({'one': 'n is 1'}) "(function(n) { return (n == 1) ? 'one' : 'other'; })" Implementation detail: The function generated will probably evaluate expressions involved into range operations multiple times. This has the advantage that external helper functions are not required and is not a big performance hit for these simple calculations. :param rule: the rules as list or dict, or a `PluralRule` object :raise RuleError: if the expression is malformed z(function(n) { return z %s ? %r : z%r; })r)_JavaScriptCompilerrBr!r>r"r. _fallback_tagr)ruleZto_jsresultr6r-rrr to_javascripts rYcCsttttd}tj}ddg}t|jD]"\}}| d||t |fq*| dt td |dd}t |||d S) a<Convert a list/dict of rules or a `PluralRule` object into a regular Python function. This is useful in situations where you need a real function and don't are about the actual rule object: >>> func = to_python({'one': 'n is 1', 'few': 'n in 2..4'}) >>> func(1) 'one' >>> func(3) 'few' >>> func = to_python({'one': 'n in 1,11', 'few': 'n in 3..10,13..19'}) >>> func(11) 'one' >>> func(15) 'few' :param rule: the rules as list or dict, or a `PluralRule` object :raise RuleError: if the expression is malformed )INZWITHINZMODr zdef evaluate(n):z' n, i, v, w, f, t = extract_operands(n)z if (%s): return %rz return %r zexecZevaluate) in_range_listwithin_range_list cldr_modulor _PythonCompilerrBr!r>r"r.r rVreval)rW namespaceZto_python_funcrXr6r-coderrr rLs rLcst|}|jthBtj}fddtDj}dtg}|j D]"\}}| d||||fqF| d|td |S)a~The plural rule as gettext expression. The gettext expression is technically limited to integers and returns indices rather than tags. >>> to_gettext({'one': 'n is 1', 'two': 'n is 2'}) 'nplurals=3; plural=((n == 1) ? 0 : (n == 2) ? 1 : 2)' :param rule: the rules as list or dict, or a `PluralRule` object :raise RuleError: if the expression is malformed csg|]}|vr|qSrrr5Z used_tagsrr r8rzto_gettext..znplurals=%d; plural=(z %s ? %d : z%d)r) r!r>rTrV_GettextCompilerrBr)indexrr"r.r)rWr@Z _get_indexrXr6r-rrdr to_gettexts  rgcCs|t|kot||S)aInteger range list test. This is the callback for the "in" operator of the UTS #35 pluralization rule language: >>> in_range_list(1, [(1, 3)]) True >>> in_range_list(3, [(1, 3)]) True >>> in_range_list(3, [(1, 3), (5, 8)]) True >>> in_range_list(1.2, [(1, 4)]) False >>> in_range_list(10, [(1, 4)]) False >>> in_range_list(10, [(1, 4), (6, 8)]) False )rr^num range_listrrr r]sr]cstfdd|DS)aFloat range test. This is the callback for the "within" operator of the UTS #35 pluralization rule language: >>> within_range_list(1, [(1, 3)]) True >>> within_range_list(1.0, [(1, 3)]) True >>> within_range_list(1.2, [(1, 4)]) True >>> within_range_list(8.8, [(1, 4), (7, 15)]) True >>> within_range_list(10, [(1, 4)]) False >>> within_range_list(10.5, [(1, 4), (20, 30)]) False c3s"|]\}}|ko|kVqdSrr)r Zmin_Zmax_rirr r$rz$within_range_list..)anyrhrrkr r^sr^cCs@d}|dkr|d9}d}|dkr(|d9}||}|r<|d9}|S)zJavaish modulo. This modulo operator returns the value with the sign of the dividend rather than the divisor like Python does: >>> cldr_modulo(-3, 5) -3 >>> cldr_modulo(-3, -5) -3 >>> cldr_modulo(3, 5) 3 rr)abreverservrrr r_'s r_c@seZdZdZdS) RuleErrorzRaised if a rule is malformed.N)r:rNrOrPrrrr rs>srsZnivwftz\s+wordz)\b(and|or|is|(?:with)?in|not|mod|[{0}])\bvaluez\d+symbolz%|,|!=|=ellipsisz\.{2,3}|\u2026cCs|dd}g}d}t|}||kr|tD]>\}}|||}|dur*|}|rd|||fqq*td||q|dddS)N@rz5malformed CLDR pluralization rule. Got unexpected %rrm)splitr_RULESmatchendr.grouprs)srXposr|tokrWr{rrr tokenize_ruleMs   rcCs,|o*|dd|ko*|dup*|dd|kS)Nrmrrnrtokenstype_rurrr test_next_token`srcCst|||r|SdSr)rpoprrrr skip_tokenes rcCs d|ffS)Nrur)rurrr value_nodejsrcCs|dfS)Nrr)namerrr ident_nodensrcCsd|fS)Nrjr)rjrrr range_list_nodersrcCs d|ffS)Nnotr)rrrrr negatevsrc@sbeZdZdZddZdddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ dS)r,uInternal parser. This class can translate a single rule into an abstract tree of tuples. It implements the following grammar:: condition = and_condition ('or' and_condition)* ('@integer' samples)? ('@decimal' samples)? 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)* value = digit+ digit = 0|1|2|3|4|5|6|7|8|9 range = value'..'value samples = sampleRange (',' sampleRange)* (',' ('…'|'...'))? sampleRange = decimalValue '~' decimalValue decimalValue = value ('.' value)? - Whitespace can occur between or around any of the above tokens. - Rules should be mutually exclusive; for a given numeric value, only one rule should apply (i.e. the condition should only be true for one of the plural rule elements). - The in and within relations can take comma-separated lists, such as: 'n in 3,5,7..15'. - Samples are ignored. The translator parses the expression on instanciation into an attribute called `ast`. cCsDt||_|jsd|_dS||_|jr@td|jdddS)NzExpected end of rule, got %rrmrn)rrr- conditionrs)r/stringrrr r4s   z_Parser.__init__NcCsft|j||}|dur|S|dur6t|dur0|p2|}|jsHtd|td||jddfdS)Nz#expected %s but end of rule reachedzexpected %s but got %rrmrn)rrreprrs)r/rruZtermtokenrrr expects z_Parser.expectcCs,|}t|jddr(d||ff}q|S)Nrtor) and_conditionrrr/oprrr rsz_Parser.conditioncCs,|}t|jddr(d||ff}q|S)Nrtand)relationrrrrrr rsz_Parser.and_conditioncCs|}t|jddr8t|jddr(dp*d||ffSt|jdd}d}t|jddr^d}n$t|jdds|rxtd||Sd|||ff}|rt|S|S) NrtisrZisnotinZwithinz#Cannot negate operator based rules.r)r3rrrursnewfangled_relationrjr)r/leftnegatedmethodrrrrr rs  z_Parser.relationcCsRt|jddrd}nt|jddr(d}ntddd||ff}|rNt|S|S) Nrv=Fz!=Tz'Expected "=" or "!=" or legacy relationrr)rrrsrjr)r/rrrrrrr rsz_Parser.newfangled_relationcCs,|}t|jdr ||fS||fSdS)Nrw)rurr)r/rrrr range_or_values  z_Parser.range_or_valuecCs0|g}t|jddr(||q t|S)Nrv,)rrrr.r)r/rjrrr rjs z_Parser.range_listcCs|t|jd}|dus |dtvr(td|d}t|jddrRd|df|ffSt|jddrtd|df|ffSt|S)NrtrnzExpected identifier variablemodrrv%)rr_VARSrsrur)r/rtrrrr r3s z _Parser.exprcCstt|ddS)Nrurn)rrrr;rrr rusz _Parser.value)NN)r:rNrOrPr4rrrrrrrjr3rurrrr r,zs!   r,cs fddS)%Compiler factory for the `_Compiler`.cs||||fSrrB)r/lrZtmplrr rFrz"_binary_compiler..rrrrr _binary_compilersrcs fddS)rcs||Srr)r/rErrr rFrz!_unary_compiler..rrrrr _unary_compilersrcCsdS)NrrrDrrr rFrrFc@seZdZdZddZddZddZddZddZd dZ d dZ d dZ e d Z e d ZedZe dZe dZe dZddZdS) _CompilerzZThe compilers are able to transform the expressions into multiple output formats. cCs|\}}t|d||S)NZcompile_)getattr)r/argrargsrrr rBsz_Compiler.compilecCsdS)NrrrDrrr rF rz_Compiler.cCsdS)NrrrDrrr rF rcCsdS)NrrrDrrr rF rcCsdS)NrrrDrrr rFrcCsdS)NrrrDrrr rFrcCsdS)NrrrDrrr rFrcCst|Srr )rErrrr rFrz (%s && %s)z (%s || %s)z(!%s)z (%s %% %s) (%s == %s)z (%s != %s)cCs tdSr)NotImplementedError)r/rr3rjrrr compile_relationsz_Compiler.compile_relationN)r:rNrOrPrB compile_n compile_i compile_v compile_w compile_f compile_tZ compile_valuer compile_and compile_orr compile_not compile_mod compile_is compile_isnotrrrrr rs rc@s8eZdZdZedZedZedZedZ ddZ dS) r`z!Compiles an expression to Python.z (%s and %s)z (%s or %s)z(not %s)z MOD(%s, %s)cs8ddfdd|dD}d|||fS)Nz[%s]rcs g|]}dttj|qS)z(%s, %s))tuplemaprB)r Zrange_r;rr r8'sz4_PythonCompiler.compile_relation..rnz %s(%s, %s))rupperrB)r/rr3rjZcompile_range_listrr;r r%s z _PythonCompiler.compile_relationN) r:rNrOrPrrrrrrrrrrr r`s r`c@s.eZdZdZejZeZeZ eZ eZ ddZ dS)rez)Compile into a gettext plural expression.c Cs~g}||}|dD]X}|d|dkrH|d|||dfqt|j|\}}|d||||fqdd|S)Nrnrrz(%s >= %s && %s <= %s)z(%s)z || )rBr.rr)r/rr3rjrritemminmaxrrr r6s     z!_GettextCompiler.compile_relationN) r:rNrOrPrrr compile_zerorrrrrrrrr re-srec@s0eZdZdZddZeZeZeZeZ ddZ dS)rUz/Compiles the expression to plain of JavaScript.cCsdS)NzparseInt(n, 10)rrDrrr rFOrz_JavaScriptCompiler.cCs4t||||}|dkr0||}d|||f}|S)Nrz(parseInt(%s, 10) == %s && %s))rerrB)r/rr3rjrcrrr rUs z$_JavaScriptCompiler.compile_relationN) r:rNrOrPrrrrrrrrrrr rUJsrUc@sJeZdZdZedZedZedZedZedZ ddZ d d d Z d S)rAz+Returns a unicode pluralization rule again.z%s is %sz %s is not %sz %s and %sz%s or %sz %s mod %scCs|j|dddiS)NrnrT)r)r/rrrr rksz_UnicodeCompiler.compile_notFcCsvg}|dD]D}|d|dkr6|||dq |dtt|j|q d|||rddpfd|d|fS)Nrnrz%s..%sz %s%s %s %sz notrr)r.rBrrr)r/rr3rjrZrangesrrrr rns  z!_UnicodeCompiler.compile_relationN)F) r:rNrOrPrrrrrrrrrrrr rA^srA)N)N)&rPreZ babel._compatrr)rVr objectr!rYrLrgr]r^r_ ExceptionrsrrBUNICODEformatrzrrrrrrrr,rrrrr`rerUrArrrr sL  8](      {