parameters: (println, variantMappings, redundantMappings, extlangMappings,
                            description, fileDate, url)
docstring: Writes a function definition which performs language tag mapping.
  
          Processes the contents of dictionaries |variantMappings| and
          |redundantMappings| through the |println| function with the given
          function name and a comment with description, fileDate, and URL.
docstring_summary: Writes a function definition which performs language tag mapping.
docstring_tokens: Writes a function definition which performs language tag mapping .
function: def writeMappingsFunction(println, variantMappings, redundantMappings, extlangMappings,
                            description, fileDate, url):
      """ Writes a function definition which performs language tag mapping.
  
          Processes the contents of dictionaries |variantMappings| and
          |redundantMappings| through the |println| function with the given
          function name and a comment with description, fileDate, and URL.
      """
  
      class Subtag(object):
          Language, ExtLang, Script, Region, Variant = range(5)
          Invalid = -1
  
      def splitSubtags(tag):
          seenLanguage = False
          for subtag in tag.split("-"):
              # language = 2*3ALPHA / 4ALPHA / 5*8ALPHA
              if len(subtag) in range(2, 8+1) and subtag.isalpha() and not seenLanguage:
                  seenLanguage = True
                  kind = Subtag.Language
  
              # extlang = 3ALPHA
              elif len(subtag) == 3 and subtag.isalpha() and seenLanguage:
                  kind = Subtag.ExtLang
  
              # script = 4ALPHA
              elif len(subtag) == 4 and subtag.isalpha():
                  kind = Subtag.Script
  
              # region = 2ALPHA / 3DIGIT
              elif ((len(subtag) == 2 and subtag.isalpha()) or
                    (len(subtag) == 3 and subtag.isdigit())):
                  kind = Subtag.Region
  
              # variant = 5*8alphanum / (DIGIT 3alphanum)
              elif ((len(subtag) in range(5, 8+1) and subtag.isalnum()) or
                    (len(subtag) == 4 and subtag[0].isdigit() and subtag[1:].isalnum())):
                  kind = Subtag.Variant
  
              else:
                  assert False, "unexpected language tag '{}'".format(subtag)
  
              yield (kind, subtag)
  
      def language(tag):
          (kind, subtag) = next(splitSubtags(tag))
          assert kind == Subtag.Language
          return subtag
  
      def variants(tag):
          return [v for (k, v) in splitSubtags(tag) if k == Subtag.Variant]
  
      def emitCompare(tag, preferred, isFirstLanguageTag):
          def println_indent(level, *args):
              println(u" " * (4 * level - 1), *args)
          println2 = partial(println_indent, 2)
          println3 = partial(println_indent, 3)
  
          def maybeNext(it):
              dummy = (Subtag.Invalid, "")
              return next(it, dummy)
  
          # Add a comment for the language tag mapping.
          println2(u"// {} -> {}".format(tag, preferred))
  
          # Compare the input language tag with the current language tag.
          cond = []
          extlangIndex = 1
          lastVariant = None
          for (kind, subtag) in splitSubtags(tag):
              if kind == Subtag.Language:
                  continue
  
              if kind == Subtag.ExtLang:
                  assert extlangIndex in [1, 2, 3],\
                      "Language-Tag permits no more than three extlang subtags"
                  cond.append('tag.extlang{} === "{}"'.format(extlangIndex, subtag))
                  extlangIndex += 1
              elif kind == Subtag.Script:
                  cond.append('tag.script === "{}"'.format(subtag))
              elif kind == Subtag.Region:
                  cond.append('tag.region === "{}"'.format(subtag))
              else:
                  assert kind == Subtag.Variant
                  if lastVariant is None:
                      cond.append("tag.variants.length >= {}".format(len(variants(tag))))
                      cond.append('callFunction(ArrayIndexOf, tag.variants, "{}") > -1'.format(
                          subtag))
                  else:
                      cond.append(
                          'callFunction(ArrayIndexOf, tag.variants, "{}", callFunction(ArrayIndexOf, tag.variants, "{}") + 1) > -1'.format(subtag, lastVariant))  # NOQA: E501
                  lastVariant = subtag
  
          # Require exact matches for redundant language tags.
          if tag in redundantMappings:
              tag_it = splitSubtags(tag)
              tag_next = partial(maybeNext, tag_it)
              (tag_kind, _) = tag_next()
  
              assert tag_kind == Subtag.Language
              (tag_kind, _) = tag_next()
  
              subtags = ([(Subtag.ExtLang, "extlang{}".format(i)) for i in range(1, 3+1)] +
                         [(Subtag.Script, "script"), (Subtag.Region, "region")])
              for kind, prop_name in subtags:
                  if tag_kind == kind:
                      (tag_kind, _) = tag_next()
                  else:
                      cond.append("tag.{} === undefined".format(prop_name))
  
              cond.append("tag.variants.length === {}".format(len(variants(tag))))
              while tag_kind == Subtag.Variant:
                  (tag_kind, _) = tag_next()
  
              cond.append("tag.extensions.length === 0")
              cond.append("tag.privateuse === undefined")
              assert list(tag_it) == [], "unhandled tag subtags"
  
          # Emit either:
          #
          #   if (cond) {
          #
          # or:
          #
          #   if (cond_1 &&
          #       cond_2 &&
          #       ...
          #       cond_n)
          #   {
          #
          # depending on the number of conditions.
          ifOrElseIf = "if" if isFirstLanguageTag else "else if"
          assert len(cond) > 0, "expect at least one subtag condition"
          if len(cond) == 1:
              println2(u"{} ({}) {{".format(ifOrElseIf, cond[0]))
          else:
              println2(u"{} ({} &&".format(ifOrElseIf, cond[0]))
              for c in cond[1:-1]:
                  println2(u"{}{} &&".format(" " * (len(ifOrElseIf) + 2), c))
              println2(u"{}{})".format(" " * (len(ifOrElseIf) + 2), cond[-1]))
              println2(u"{")
  
          # Iterate over all subtags of |tag| and |preferred| and update |tag|
          # with |preferred| in the process. |tag| is modified in-place to use
          # the preferred values.
          tag_it = splitSubtags(tag)
          tag_next = partial(maybeNext, tag_it)
          (tag_kind, tag_subtag) = tag_next()
  
          preferred_it = splitSubtags(preferred)
          preferred_next = partial(maybeNext, preferred_it)
          (preferred_kind, preferred_subtag) = preferred_next()
  
          # Update the language subtag.
          assert tag_kind == Subtag.Language and preferred_kind == Subtag.Language
          if tag_subtag != preferred_subtag:
              println3(u'tag.language = "{}";'.format(preferred_subtag))
          (tag_kind, tag_subtag) = tag_next()
          (preferred_kind, preferred_subtag) = preferred_next()
  
          # Remove any extlang subtags per RFC 5646, 4.5:
          # 'The canonical form contains no 'extlang' subtags.'
          # https://tools.ietf.org/html/rfc5646#section-4.5
          assert preferred_kind != Subtag.ExtLang
          extlangIndex = 1
          while tag_kind == Subtag.ExtLang:
              assert extlangIndex in [1, 2, 3],\
                  "Language-Tag permits no more than three extlang subtags"
              println3(u"tag.extlang{} = undefined;".format(extlangIndex))
              extlangIndex += 1
              (tag_kind, tag_subtag) = tag_next()
  
          # Update the script and region subtags.
          for kind, prop_name in [(Subtag.Script, "script"), (Subtag.Region, "region")]:
              if tag_kind == kind and preferred_kind == kind:
                  if tag_subtag != preferred_subtag:
                      println3(u'tag.{} = "{}";'.format(prop_name, preferred_subtag))
                  (tag_kind, tag_subtag) = tag_next()
                  (preferred_kind, preferred_subtag) = preferred_next()
              elif tag_kind == kind:
                  println3(u"tag.{} = undefined;".format(prop_name))
                  (tag_kind, tag_subtag) = tag_next()
              elif preferred_kind == kind:
                  println3(u'tag.{} = "{}";'.format(prop_name, preferred_subtag))
                  (preferred_kind, preferred_subtag) = preferred_next()
  
          # Update variant subtags.
          if tag_kind == Subtag.Variant or preferred_kind == Subtag.Variant:
              # JS doesn't provide an easy way to remove elements from an array
              # which doesn't trigger Symbol.species, so we need to create a new
              # array and copy all elements.
              println3(u"var newVariants = [];")
  
              # Copy all variant subtags, ignoring those which should be removed.
              println3(u"for (var i = 0; i < tag.variants.length; i++) {")
              println3(u"    var variant = tag.variants[i];")
              while tag_kind == Subtag.Variant:
                  println3(u'    if (variant === "{}")'.format(tag_subtag))
                  println3(u"        continue;")
                  (tag_kind, tag_subtag) = tag_next()
              println3(u"    _DefineDataProperty(newVariants, newVariants.length, variant);")
              println3(u"}")
  
              # Add the new variants, unless already present.
              while preferred_kind == Subtag.Variant:
                  println3(u'if (callFunction(ArrayIndexOf, newVariants, "{}") < 0)'.format(
                      preferred_subtag))
                  println3(u'    _DefineDataProperty(newVariants, newVariants.length, "{}");'.format(
                      preferred_subtag))
                  (preferred_kind, preferred_subtag) = preferred_next()
  
              # Update the property.
              println3(u"tag.variants = newVariants;")
  
          # Ensure both language tags were completely processed.
          assert list(tag_it) == [], "unhandled tag subtags"
          assert list(preferred_it) == [], "unhandled preferred subtags"
  
          println2(u"}")
  
      # Remove mappings for redundant language tags which are from our point of
      # view, wait for it, redundant, because there is an equivalent extlang
      # mapping.
      #
      # For example this entry for the redundant tag "zh-cmn":
      #
      #   Type: redundant
      #   Tag: zh-cmn
      #   Preferred-Value: cmn
      #
      # Can also be expressed through the extlang mapping for "cmn":
      #
      #   Type: extlang
      #   Subtag: cmn
      #   Preferred-Value: cmn
      #   Prefix: zh
      #
      def hasExtlangMapping(tag, preferred):
          tag_it = splitSubtags(tag)
          (_, tag_lang) = next(tag_it)
          (tag_kind, tag_extlang) = next(tag_it)
  
          preferred_it = splitSubtags(preferred)
          (_, preferred_lang) = next(preferred_it)
  
          # Return true if the mapping is for an extlang language and the extlang
          # mapping table contains an equivalent entry and any trailing elements,
          # if present, are the same.
          return (tag_kind == Subtag.ExtLang and
                  (tag_extlang, {"preferred": preferred_lang, "prefix": tag_lang}) in
                  extlangMappings.items() and
                  list(tag_it) == list(preferred_it))
  
      # Create a single mapping for variant and redundant tags, ignoring the
      # entries which are also covered through extlang mappings.
      langTagMappings = {tag: preferred
                         for mapping in [variantMappings, redundantMappings]
                         for (tag, preferred) in mapping.items()
                         if not hasExtlangMapping(tag, preferred)}
  
      println(u"")
      println(u"/* eslint-disable complexity */")
      writeMappingHeader(println, description, fileDate, url)
      println(u"function updateLangTagMappings(tag) {")
      println(u'    assert(IsObject(tag), "tag is an object");')
      println(u'    assert(!hasOwn("grandfathered", tag), "tag is not a grandfathered tag");')
      println(u"")
  
      # Switch on the language subtag.
      println(u"    switch (tag.language) {")
      for lang in sorted({language(tag) for tag in langTagMappings}):
          println(u'      case "{}":'.format(lang))
          isFirstLanguageTag = True
          for tag in sorted(tag for tag in langTagMappings if language(tag) == lang):
              assert not isinstance(langTagMappings[tag], dict),\
                  "only supports complete language tags"
              emitCompare(tag, langTagMappings[tag], isFirstLanguageTag)
              isFirstLanguageTag = False
          println(u"        break;")
      println(u"    }")
  
      println(u"}")
      println(u"/* eslint-enable complexity */")
function_tokens: def writeMappingsFunction ( println , variantMappings , redundantMappings , extlangMappings , description , fileDate , url ) : class Subtag ( object ) : Language , ExtLang , Script , Region , Variant = range ( 5 ) Invalid = - 1 def splitSubtags ( tag ) : seenLanguage = False for subtag in tag . split ( "-" ) : # language = 2*3ALPHA / 4ALPHA / 5*8ALPHA if len ( subtag ) in range ( 2 , 8 + 1 ) and subtag . isalpha ( ) and not seenLanguage : seenLanguage = True kind = Subtag . Language # extlang = 3ALPHA elif len ( subtag ) == 3 and subtag . isalpha ( ) and seenLanguage : kind = Subtag . ExtLang # script = 4ALPHA elif len ( subtag ) == 4 and subtag . isalpha ( ) : kind = Subtag . Script # region = 2ALPHA / 3DIGIT elif ( ( len ( subtag ) == 2 and subtag . isalpha ( ) ) or ( len ( subtag ) == 3 and subtag . isdigit ( ) ) ) : kind = Subtag . Region # variant = 5*8alphanum / (DIGIT 3alphanum) elif ( ( len ( subtag ) in range ( 5 , 8 + 1 ) and subtag . isalnum ( ) ) or ( len ( subtag ) == 4 and subtag [ 0 ] . isdigit ( ) and subtag [ 1 : ] . isalnum ( ) ) ) : kind = Subtag . Variant else : assert False , "unexpected language tag '{}'" . format ( subtag ) yield ( kind , subtag ) def language ( tag ) : ( kind , subtag ) = next ( splitSubtags ( tag ) ) assert kind == Subtag . Language return subtag def variants ( tag ) : return [ v for ( k , v ) in splitSubtags ( tag ) if k == Subtag . Variant ] def emitCompare ( tag , preferred , isFirstLanguageTag ) : def println_indent ( level , * args ) : println ( u" " * ( 4 * level - 1 ) , * args ) println2 = partial ( println_indent , 2 ) println3 = partial ( println_indent , 3 ) def maybeNext ( it ) : dummy = ( Subtag . Invalid , "" ) return next ( it , dummy ) # Add a comment for the language tag mapping. println2 ( u"// {} -> {}" . format ( tag , preferred ) ) # Compare the input language tag with the current language tag. cond = [ ] extlangIndex = 1 lastVariant = None for ( kind , subtag ) in splitSubtags ( tag ) : if kind == Subtag . Language : continue if kind == Subtag . ExtLang : assert extlangIndex in [ 1 , 2 , 3 ] , "Language-Tag permits no more than three extlang subtags" cond . append ( 'tag.extlang{} === "{}"' . format ( extlangIndex , subtag ) ) extlangIndex += 1 elif kind == Subtag . Script : cond . append ( 'tag.script === "{}"' . format ( subtag ) ) elif kind == Subtag . Region : cond . append ( 'tag.region === "{}"' . format ( subtag ) ) else : assert kind == Subtag . Variant if lastVariant is None : cond . append ( "tag.variants.length >= {}" . format ( len ( variants ( tag ) ) ) ) cond . append ( 'callFunction(ArrayIndexOf, tag.variants, "{}") > -1' . format ( subtag ) ) else : cond . append ( 'callFunction(ArrayIndexOf, tag.variants, "{}", callFunction(ArrayIndexOf, tag.variants, "{}") + 1) > -1' . format ( subtag , lastVariant ) ) # NOQA: E501 lastVariant = subtag # Require exact matches for redundant language tags. if tag in redundantMappings : tag_it = splitSubtags ( tag ) tag_next = partial ( maybeNext , tag_it ) ( tag_kind , _ ) = tag_next ( ) assert tag_kind == Subtag . Language ( tag_kind , _ ) = tag_next ( ) subtags = ( [ ( Subtag . ExtLang , "extlang{}" . format ( i ) ) for i in range ( 1 , 3 + 1 ) ] + [ ( Subtag . Script , "script" ) , ( Subtag . Region , "region" ) ] ) for kind , prop_name in subtags : if tag_kind == kind : ( tag_kind , _ ) = tag_next ( ) else : cond . append ( "tag.{} === undefined" . format ( prop_name ) ) cond . append ( "tag.variants.length === {}" . format ( len ( variants ( tag ) ) ) ) while tag_kind == Subtag . Variant : ( tag_kind , _ ) = tag_next ( ) cond . append ( "tag.extensions.length === 0" ) cond . append ( "tag.privateuse === undefined" ) assert list ( tag_it ) == [ ] , "unhandled tag subtags" # Emit either: # #   if (cond) { # # or: # #   if (cond_1 && #       cond_2 && #       ... #       cond_n) #   { # # depending on the number of conditions. ifOrElseIf = "if" if isFirstLanguageTag else "else if" assert len ( cond ) > 0 , "expect at least one subtag condition" if len ( cond ) == 1 : println2 ( u"{} ({}) {{" . format ( ifOrElseIf , cond [ 0 ] ) ) else : println2 ( u"{} ({} &&" . format ( ifOrElseIf , cond [ 0 ] ) ) for c in cond [ 1 : - 1 ] : println2 ( u"{}{} &&" . format ( " " * ( len ( ifOrElseIf ) + 2 ) , c ) ) println2 ( u"{}{})" . format ( " " * ( len ( ifOrElseIf ) + 2 ) , cond [ - 1 ] ) ) println2 ( u"{" ) # Iterate over all subtags of |tag| and |preferred| and update |tag| # with |preferred| in the process. |tag| is modified in-place to use # the preferred values. tag_it = splitSubtags ( tag ) tag_next = partial ( maybeNext , tag_it ) ( tag_kind , tag_subtag ) = tag_next ( ) preferred_it = splitSubtags ( preferred ) preferred_next = partial ( maybeNext , preferred_it ) ( preferred_kind , preferred_subtag ) = preferred_next ( ) # Update the language subtag. assert tag_kind == Subtag . Language and preferred_kind == Subtag . Language if tag_subtag != preferred_subtag : println3 ( u'tag.language = "{}";' . format ( preferred_subtag ) ) ( tag_kind , tag_subtag ) = tag_next ( ) ( preferred_kind , preferred_subtag ) = preferred_next ( ) # Remove any extlang subtags per RFC 5646, 4.5: # 'The canonical form contains no 'extlang' subtags.' # https://tools.ietf.org/html/rfc5646#section-4.5 assert preferred_kind != Subtag . ExtLang extlangIndex = 1 while tag_kind == Subtag . ExtLang : assert extlangIndex in [ 1 , 2 , 3 ] , "Language-Tag permits no more than three extlang subtags" println3 ( u"tag.extlang{} = undefined;" . format ( extlangIndex ) ) extlangIndex += 1 ( tag_kind , tag_subtag ) = tag_next ( ) # Update the script and region subtags. for kind , prop_name in [ ( Subtag . Script , "script" ) , ( Subtag . Region , "region" ) ] : if tag_kind == kind and preferred_kind == kind : if tag_subtag != preferred_subtag : println3 ( u'tag.{} = "{}";' . format ( prop_name , preferred_subtag ) ) ( tag_kind , tag_subtag ) = tag_next ( ) ( preferred_kind , preferred_subtag ) = preferred_next ( ) elif tag_kind == kind : println3 ( u"tag.{} = undefined;" . format ( prop_name ) ) ( tag_kind , tag_subtag ) = tag_next ( ) elif preferred_kind == kind : println3 ( u'tag.{} = "{}";' . format ( prop_name , preferred_subtag ) ) ( preferred_kind , preferred_subtag ) = preferred_next ( ) # Update variant subtags. if tag_kind == Subtag . Variant or preferred_kind == Subtag . Variant : # JS doesn't provide an easy way to remove elements from an array # which doesn't trigger Symbol.species, so we need to create a new # array and copy all elements. println3 ( u"var newVariants = [];" ) # Copy all variant subtags, ignoring those which should be removed. println3 ( u"for (var i = 0; i < tag.variants.length; i++) {" ) println3 ( u"    var variant = tag.variants[i];" ) while tag_kind == Subtag . Variant : println3 ( u'    if (variant === "{}")' . format ( tag_subtag ) ) println3 ( u"        continue;" ) ( tag_kind , tag_subtag ) = tag_next ( ) println3 ( u"    _DefineDataProperty(newVariants, newVariants.length, variant);" ) println3 ( u"}" ) # Add the new variants, unless already present. while preferred_kind == Subtag . Variant : println3 ( u'if (callFunction(ArrayIndexOf, newVariants, "{}") < 0)' . format ( preferred_subtag ) ) println3 ( u'    _DefineDataProperty(newVariants, newVariants.length, "{}");' . format ( preferred_subtag ) ) ( preferred_kind , preferred_subtag ) = preferred_next ( ) # Update the property. println3 ( u"tag.variants = newVariants;" ) # Ensure both language tags were completely processed. assert list ( tag_it ) == [ ] , "unhandled tag subtags" assert list ( preferred_it ) == [ ] , "unhandled preferred subtags" println2 ( u"}" ) # Remove mappings for redundant language tags which are from our point of # view, wait for it, redundant, because there is an equivalent extlang # mapping. # # For example this entry for the redundant tag "zh-cmn": # #   Type: redundant #   Tag: zh-cmn #   Preferred-Value: cmn # # Can also be expressed through the extlang mapping for "cmn": # #   Type: extlang #   Subtag: cmn #   Preferred-Value: cmn #   Prefix: zh # def hasExtlangMapping ( tag , preferred ) : tag_it = splitSubtags ( tag ) ( _ , tag_lang ) = next ( tag_it ) ( tag_kind , tag_extlang ) = next ( tag_it ) preferred_it = splitSubtags ( preferred ) ( _ , preferred_lang ) = next ( preferred_it ) # Return true if the mapping is for an extlang language and the extlang # mapping table contains an equivalent entry and any trailing elements, # if present, are the same. return ( tag_kind == Subtag . ExtLang and ( tag_extlang , { "preferred" : preferred_lang , "prefix" : tag_lang } ) in extlangMappings . items ( ) and list ( tag_it ) == list ( preferred_it ) ) # Create a single mapping for variant and redundant tags, ignoring the # entries which are also covered through extlang mappings. langTagMappings = { tag : preferred for mapping in [ variantMappings , redundantMappings ] for ( tag , preferred ) in mapping . items ( ) if not hasExtlangMapping ( tag , preferred ) } println ( u"" ) println ( u"/* eslint-disable complexity */" ) writeMappingHeader ( println , description , fileDate , url ) println ( u"function updateLangTagMappings(tag) {" ) println ( u'    assert(IsObject(tag), "tag is an object");' ) println ( u'    assert(!hasOwn("grandfathered", tag), "tag is not a grandfathered tag");' ) println ( u"" ) # Switch on the language subtag. println ( u"    switch (tag.language) {" ) for lang in sorted ( { language ( tag ) for tag in langTagMappings } ) : println ( u'      case "{}":' . format ( lang ) ) isFirstLanguageTag = True for tag in sorted ( tag for tag in langTagMappings if language ( tag ) == lang ) : assert not isinstance ( langTagMappings [ tag ] , dict ) , "only supports complete language tags" emitCompare ( tag , langTagMappings [ tag ] , isFirstLanguageTag ) isFirstLanguageTag = False println ( u"        break;" ) println ( u"    }" ) println ( u"}" ) println ( u"/* eslint-enable complexity */" )