docs: def parse_args():
      """
      Parse commandline arguments.
      """
      def exclusive_group(group, name, default, help):
          destname = name.replace('-', '_')
          subgroup = group.add_mutually_exclusive_group(required=False)
          subgroup.add_argument(f'--{name}', dest=f'{destname}',
                                action='store_true',
                                help=f'{help} (use \'--no-{name}\' to disable)')
          subgroup.add_argument(f'--no-{name}', dest=f'{destname}',
                                action='store_false', help=argparse.SUPPRESS)
          subgroup.set_defaults(**{destname: default})
  
      parser = argparse.ArgumentParser(
          description='GNMT Translate',
          formatter_class=argparse.ArgumentDefaultsHelpFormatter)
  
      # dataset
      dataset = parser.add_argument_group('data setup')
      dataset.add_argument('--dataset-dir', default='data/wmt16_de_en/',
                           help='path to directory with training/test data')
      dataset.add_argument('-i', '--input', required=True,
                           help='full path to the input file (tokenized)')
      dataset.add_argument('-o', '--output', required=True,
                           help='full path to the output file (tokenized)')
      dataset.add_argument('-r', '--reference', default=None,
                           help='full path to the file with reference \
                           translations (for sacrebleu)')
      dataset.add_argument('-m', '--model', required=True,
                           help='full path to the model checkpoint file')
      exclusive_group(group=dataset, name='sort', default=True,
                      help='sorts dataset by sequence length')
  
      # parameters
      params = parser.add_argument_group('inference setup')
      params.add_argument('--batch-size', nargs='+', default=[128], type=int,
                          help='batch size per GPU')
      params.add_argument('--beam-size', nargs='+', default=[5], type=int,
                          help='beam size')
      params.add_argument('--max-seq-len', default=80, type=int,
                          help='maximum generated sequence length')
      params.add_argument('--len-norm-factor', default=0.6, type=float,
                          help='length normalization factor')
      params.add_argument('--cov-penalty-factor', default=0.1, type=float,
                          help='coverage penalty factor')
      params.add_argument('--len-norm-const', default=5.0, type=float,
                          help='length normalization constant')
      # general setup
      general = parser.add_argument_group('general setup')
      general.add_argument('--math', nargs='+', default=['fp16'],
                           choices=['fp16', 'fp32'], help='arithmetic type')
  
      exclusive_group(group=general, name='env', default=True,
                      help='print info about execution env')
      exclusive_group(group=general, name='bleu', default=True,
                      help='compares with reference translation and computes \
                      BLEU')
      exclusive_group(group=general, name='cuda', default=True,
                      help='enables cuda')
      exclusive_group(group=general, name='cudnn', default=True,
                      help='enables cudnn')
  
      batch_first_parser = general.add_mutually_exclusive_group(required=False)
      batch_first_parser.add_argument('--batch-first', dest='batch_first',
                                      action='store_true',
                                      help='uses (batch, seq, feature) data \
                                      format for RNNs')
      batch_first_parser.add_argument('--seq-first', dest='batch_first',
                                      action='store_false',
                                      help='uses (seq, batch, feature) data \
                                      format for RNNs')
      batch_first_parser.set_defaults(batch_first=True)
  
      general.add_argument('--print-freq', '-p', default=1, type=int,
                           help='print log every PRINT_FREQ batches')
  
      # distributed
      distributed = parser.add_argument_group('distributed setup')
      distributed.add_argument('--rank', default=0, type=int,
                               help='global rank of the process, do not set!')
      distributed.add_argument('--local_rank', default=0, type=int,
                               help='local rank of the process, do not set!')
  
      args = parser.parse_args()
  
      if args.bleu and args.reference is None:
          parser.error('--bleu requires --reference')
  
      if 'fp16' in args.math and not args.cuda:
          parser.error('--math fp16 requires --cuda')
  
      return args