Skip to content

Change how command lines are parsed into args. #6703

@NTT1906

Description

@NTT1906

Problem description

The current SimpleCommandMap::dispatch() use CommandHelper::parseQuoteAware() to parse command line to args, currently this following command line

  • /tellraw @a[name="Arie1906", c=1] "quoted \"你好\"" {"rawtext": [{"text":"§bI am blue"}]} would be parsed to [/tellraw, @a[name="Arie1906",, c=1], quoted "你好", {"rawtext":, [{"text":"§bI, am, blue"}]}]
  • Issues:
    • The returned args are messy and require extra complexity to capture those grouped tags/json input ([...], {...} or (...))
    • The args have lost those spaces between I, am, blue; despite it being in a quote.
  • IMO, it's should ideally return [/tellraw, @a[name="Arie1906, c=1], quoted "你好", {"rawtext": [{"text":"§bI am blue"}]}]
  • The result above output from this following code scaped out of PMMP: RUN
<?php
function parseQuoteAware(string $commandLine) : array{
  $args = [];
  preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|(\S+)/u', $commandLine, $matches);
  foreach($matches[0] as $k => $_){
    for($i = 1; $i <= 2; ++$i){
      if($matches[$i][$k] !== ""){
        $match = $matches[$i][$k];
        $args[] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
        break;
      }
    }
  }
  return $args;
}

$cmdLine = '/tellraw @a[name="Arie1906", c=1] "quoted \"string\"" {"rawtext": [{"text":"§bI     am blue"}]}';
var_dump(parseQuoteAware($cmdLine));

Proposed solution

This following will parse the command line exactly as I wanted while having no effects on performance. RUN

function parseQuoteAware(string $commandLine) : array{
  $args = [];
  preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|[~^][^\s~^]*|\S+\s*/u', $commandLine, $matches, PREG_UNMATCHED_AS_NULL);
  $size = count($matches[0]);
  for ($k = 0; $k < $size; ++$k) {
    if ($matches[1][$k] !== null) {
      $args[] = str_replace('\"', '"', $matches[1][$k]);
      continue;
    }
    $match = $matches[0][$k];
    $level = (substr_count($match, "[") - substr_count($match, "]"))
           + (substr_count($match, "{") - substr_count($match, "}"))
           + (substr_count($match, "(") - substr_count($match, ")"));
    if ($level == 0 || $k == $size - 1) {
      $args[] = rtrim($match);
    } else {
      $matches[0][$k + 1] = $match . $matches[0][$k + 1];
    }
  }
  return $args;
}

Alternative solutions or workarounds

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Category: APIRelated to the plugin APIOpinions WantedRequest for comments & opinions from the communityResolution: ObsoleteSuperseded by other changesType: EnhancementContributes features or other improvements to PocketMine-MP

    Type

    No type

    Projects

    Status

    Abandoned

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions