docs: def finalize(self) -> None:
          ''' split statement and task by last directive '''
          self.wrap_script()
          if not self.statements:
              self.task = ''
              return
          # handle tasks
          input_directive = [
              idx for idx, statement in enumerate(self.statements)
              if statement[0] == ':' and statement[1] == 'input'
          ]
          task_directive = [
              idx for idx, statement in enumerate(self.statements)
              if statement[0] == ':' and statement[1] == 'task'
          ]
          if len(task_directive) > 1:
              raise ValueError('Only one task statement is allowed in a step')
          # handle parameter
          for idx, statement in enumerate(self.statements):
              if statement[0] == ':' and statement[1] == 'parameter':
                  if task_directive and task_directive[0] < idx:
                      raise ValueError(
                          'Parameter statement is not allowed in tasks.')
                  if '=' not in statement[2]:
                      if ':' in statement[2]:
                          if not is_type_hint(statement[2]):
                              raise ValueError(
                                  f'Invalid type trait in parameter specification {statement[2]}'
                              )
                          name, value = statement[2].split(':')
                      else:
                          name = statement[2]
                          value = 'str'
                  else:
                      name, value = statement[2].split('=', 1)
                      # ignore type trait if a default value is specified
                      name = name.split(':')[0]
                  name = name.strip()
                  if name.startswith('_'):
                      raise ValueError(
                          f'Invalid parameter name {name}: names with leading underscore is not allowed.'
                      )
                  if not value.strip():
                      raise ValueError(
                          f'{self.step_name()}: Invalid parameter definition: {statement[2]}'
                      )
                  # there is a possibility that value contains # so  sos_handle_parameter(name, val # aaa) will fail
                  self.statements[idx] = [
                      '!',
                      f'#begin_parameter {name}\n{name} = sos_handle_parameter_({name.strip()!r}, {value}\n) #end_parameter {name}\n',
                      statement[2].strip()
                  ]
                  self.parameters[name] = (value, statement[3])
                  if input_directive and input_directive[0] < idx:
                      self.substep_parameters.add(name)
          # handle tasks
          if not task_directive:
              self.task = ''
          else:
              start_task = task_directive[0] + 1
              # convert statement to task
              self.task = ''
              for statement in self.statements[start_task:]:
                  if statement[0] == ':':
                      if statement[1] in ('input', 'output', 'depends'):
                          raise ValueError(
                              f'{self.step_name()}: Step task should be defined as the last item in a SoS step'
                          )
                      elif statement[1] == 'task':
                          raise ValueError(
                              f'{self.step_name()}: Only one task is allowed for a step'
                          )
                      elif statement[1] == 'parameter':
                          raise ValueError(
                              f'{self.step_name()}: Parameters should be defined before step task'
                          )
                      # ignore ...
                      self.task += '\n'
                  else:
                      self.task += statement[1]
              self.task_params = self.statements[task_directive[0]][2]
              self.statements = self.statements[:task_directive[0]]
          # merge multiple statments at the end
          if len(self.statements) > 1 and self.statements[-1][0] == '!':
              starting = len(self.statements) - 1
              for idx in range(starting - 1, -1, -1):
                  if self.statements[idx][0] == '!':
                      starting = idx
                  else:
                      break
              # merge
              for idx in range(starting + 1, len(self.statements)):
                  self.statements[starting][1] += self.statements[idx][1]
              # remove the rest of the statements
              self.statements = self.statements[:starting + 1]
          #
          # auto provides #859
          if not any(opt in self.options for opt in ('provides', 'shared')) and \
                  len([x for x in self.statements if x[0] == ':' and x[1] == 'output']) == 1:
              output_stmt = [
                  x for x in self.statements if x[0] == ':' and x[1] == 'output'
              ][0][2]
              output_names = get_names_of_kwargs(output_stmt)
              self.options['namedprovides'] = repr(output_names)