From 2efb0687f19af1e2c9141d44ee2d4dac9b652720 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Tue, 23 Sep 2025 02:49:32 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/app/cli/AbstractArgsParser.php | 84 ++++++++++++++ src/app/cli/ArgDefs.php | 58 +--------- src/app/cli/SimpleArgDefs.php | 154 +------------------------- src/app/cli/SimpleArgsParser.php | 170 +++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 204 deletions(-) create mode 100644 src/app/cli/AbstractArgsParser.php create mode 100644 src/app/cli/SimpleArgsParser.php diff --git a/src/app/cli/AbstractArgsParser.php b/src/app/cli/AbstractArgsParser.php new file mode 100644 index 0000000..36e007b --- /dev/null +++ b/src/app/cli/AbstractArgsParser.php @@ -0,0 +1,84 @@ + 0) { + throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)"); + } + } + + abstract function normalize(array $args): array; + + /** @var object|array objet destination */ + protected $dest; + + protected function setDest(&$dest): void { + $this->dest =& $dest; + } + + protected function unsetDest(): void { + unset($this->dest); + } + + abstract function process(array $args); + + function parse(&$dest, array $args=null): void { + if ($args === null) { + global $argv; + $args = array_slice($argv, 1); + } + $args = $this->normalize($args); + $dest ??= new stdClass(); + $this->setDest($dest); + $this->process($args); + $this->unsetDest(); + } +} diff --git a/src/app/cli/ArgDefs.php b/src/app/cli/ArgDefs.php index 9944349..e41f69f 100644 --- a/src/app/cli/ArgDefs.php +++ b/src/app/cli/ArgDefs.php @@ -3,6 +3,9 @@ namespace nulib\app\cli; use nulib\cl; +/** + * Class ArgDefs: une liste d'objets ArgDef + */ abstract class ArgDefs implements IArgo { function __construct(array $defs) { $this->defs = $defs; @@ -64,61 +67,6 @@ abstract class ArgDefs implements IArgo { return null; } - /** - * consommer les arguments de $src en avançant l'index $srci et provisionner - * $dest à partir de $desti. si $desti est plus grand que 0, celà veut dire - * que $dest a déjà commencé à être provisionné, et qu'il faut continuer. - * - * $destmin est le nombre minimum d'arguments à consommer. $destmax est le - * nombre maximum d'arguments à consommer. - * - * $srci est la position de l'élément courant à consommer le cas échéant - * retourner le nombre d'arguments qui manquent (ou 0 si tous les arguments - * ont été consommés) - * - * pour les arguments optionnels, ils sont consommés tant qu'il y en a de - * disponible, ou jusqu'à la présence de '--'. Si $keepsep, l'argument '--' - * est gardé dans la liste des arguments optionnels. - */ - protected static function consume_args($src, &$srci, &$dest, $desti, $destmin, $destmax, bool $keepsep): int { - $srcmax = count($src); - # arguments obligatoires - while ($desti < $destmin) { - if ($srci < $srcmax) { - $dest[] = $src[$srci]; - } else { - # pas assez d'arguments - return $destmin - $desti; - } - $srci++; - $desti++; - } - # arguments facultatifs - while ($desti < $destmax && $srci < $srcmax) { - $opt = $src[$srci]; - if ($opt === "--") { - # fin des options facultatives - if ($keepsep) $dest[] = $opt; - $srci++; - break; - } - $dest[] = $opt; - $srci++; - $desti++; - } - return 0; - } - - protected static function check_missing(?string $option, int $count) { - if ($count > 0) { - throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)"); - } - } - - function normalize(array $args): array { - return $args; - } - function debugInfos(): array { return array_map(function (IArgo $argo) { return $argo->debugInfos(); diff --git a/src/app/cli/SimpleArgDefs.php b/src/app/cli/SimpleArgDefs.php index a49e887..e384081 100644 --- a/src/app/cli/SimpleArgDefs.php +++ b/src/app/cli/SimpleArgDefs.php @@ -55,6 +55,11 @@ class SimpleArgDefs extends ArgDefs { protected array $index; + /** @return string[] */ + function getOptions(): array { + return array_keys($this->index); + } + protected function setArgos(?array $argos): void { $argos ??= []; @@ -117,155 +122,6 @@ class SimpleArgDefs extends ArgDefs { return $this->index[$option] ?? null; } - function normalize(array $args): array { - $i = 0; - $max = count($args); - $options = []; - $remains = []; - $parseOpts = true; - while ($i < $max) { - $arg = $args[$i++]; - if (!$parseOpts) { - # le reste n'est que des arguments - $remains[] = $arg; - continue; - } - if ($arg === "--") { - # fin des options - $parseOpts = false; - continue; - } - - if (substr($arg, 0, 2) === "--") { - ####################################################################### - # option longue - $pos = strpos($arg, "="); - if ($pos !== false) { - # option avec valeur - $option = substr($arg, 0, $pos); - $value = substr($arg, $pos + 1); - } else { - # option sans valeur - $option = $arg; - $value = null; - } - /** @var ArgDef $argDef */ - $argDef = $this->index[$option] ?? null; - if ($argDef === null) { - # chercher une correspondance - $len = strlen($option); - $candidates = []; - foreach (array_keys($this->index) as $candidate) { - if (substr($candidate, 0, $len) === $option) { - $candidates[] = $candidate; - } - } - switch (count($candidates)) { - case 0: - throw new ArgException("$option: option invalide"); - case 1: - $option = $candidates[0]; - break; - default: - $candidates = implode(", ", $candidates); - throw new ArgException("$option: option ambigue (les options possibles sont $candidates)"); - } - $argDef = $this->index[$option]; - } - - if ($argDef->haveArgs) { - $minArgs = $argDef->minArgs; - $maxArgs = $argDef->maxArgs; - $values = []; - if ($value !== null) { - $values[] = $value; - $offset = 1; - } elseif ($minArgs == 0) { - # cas particulier: la première valeur doit être collée à l'option - # si $maxArgs == 1 - $offset = $maxArgs == 1 ? 1 : 0; - } else { - $offset = 0; - } - $this->check_missing($option, - self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true)); - - if ($minArgs == 0 && $maxArgs == 1) { - # cas particulier: la première valeur doit être collée à l'option - if (count($values) > 0) { - $options[] = "$option=$values[0]"; - $values = array_slice($values, 1); - } else { - $options[] = $option; - } - } else { - $options[] = $option; - } - $options = array_merge($options, $values); - } elseif ($value !== null) { - throw new ArgException("$option: cette option ne prend pas d'arguments"); - } else { - $options[] = $option; - } - - } elseif (substr($arg, 0, 1) === "-") { - ####################################################################### - # option courte - $pos = 1; - $len = strlen($arg); - while ($pos < $len) { - $option = "-".substr($arg, $pos, 1); - /** @var ArgDef $argDef */ - $argDef = $this->index[$option] ?? null; - if ($argDef === null) { - throw new ArgException("$option: option invalide"); - } - if ($argDef->haveArgs) { - $minArgs = $argDef->minArgs; - $maxArgs = $argDef->maxArgs; - $values = []; - if ($len > $pos + 1) { - $values[] = substr($arg, $pos + 1); - $offset = 1; - $pos = $len; - } elseif ($minArgs == 0) { - # cas particulier: la première valeur doit être collée à l'option - # si $maxArgs == 1 - $offset = $maxArgs == 1 ? 1 : 0; - } else { - $offset = 0; - } - $this->check_missing($option, - self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true)); - - if ($minArgs == 0 && $maxArgs == 1) { - # cas particulier: la première valeur doit être collée à l'option - if (count($values) > 0) { - $options[] = "$option$values[0]"; - $values = array_slice($values, 1); - } else { - $options[] = $option; - } - } else { - $options[] = $option; - } - $options = array_merge($options, $values); - } else { - $options[] = $option; - } - $pos++; - } - } else { - #XXX implémenter les commandes - - ####################################################################### - # argument - $remains[] = $arg; - } - } - return array_merge($options, ["--"], $remains); - } - function debugInfos(): array { return [ "argos" => array_map(function (IArgo $argo) { diff --git a/src/app/cli/SimpleArgsParser.php b/src/app/cli/SimpleArgsParser.php new file mode 100644 index 0000000..abcc9cb --- /dev/null +++ b/src/app/cli/SimpleArgsParser.php @@ -0,0 +1,170 @@ +argDefs = new SimpleArgDefs($defs); + } + + protected SimpleArgDefs $argDefs; + + protected function getArgDef(string $option): ?ArgDef { + return $this->argDefs->get($option); + } + + protected function getOptions(): array { + return $this->argDefs->getOptions(); + } + + function normalize(array $args): array { + $i = 0; + $max = count($args); + $options = []; + $remains = []; + $parseOpts = true; + while ($i < $max) { + $arg = $args[$i++]; + if (!$parseOpts) { + # le reste n'est que des arguments + $remains[] = $arg; + continue; + } + if ($arg === "--") { + # fin des options + $parseOpts = false; + continue; + } + + if (substr($arg, 0, 2) === "--") { + ####################################################################### + # option longue + $pos = strpos($arg, "="); + if ($pos !== false) { + # option avec valeur + $option = substr($arg, 0, $pos); + $value = substr($arg, $pos + 1); + } else { + # option sans valeur + $option = $arg; + $value = null; + } + $argDef = $this->getArgDef($option); + if ($argDef === null) { + # chercher une correspondance + $len = strlen($option); + $candidates = []; + foreach ($this->getOptions() as $candidate) { + if (substr($candidate, 0, $len) === $option) { + $candidates[] = $candidate; + } + } + switch (count($candidates)) { + case 0: + throw new ArgException("$option: option invalide"); + case 1: + $option = $candidates[0]; + break; + default: + $candidates = implode(", ", $candidates); + throw new ArgException("$option: option ambigue (les options possibles sont $candidates)"); + } + $argDef = $this->getArgDef($option); + } + + if ($argDef->haveArgs) { + $minArgs = $argDef->minArgs; + $maxArgs = $argDef->maxArgs; + $values = []; + if ($value !== null) { + $values[] = $value; + $offset = 1; + } elseif ($minArgs == 0) { + # cas particulier: la première valeur doit être collée à l'option + # si $maxArgs == 1 + $offset = $maxArgs == 1 ? 1 : 0; + } else { + $offset = 0; + } + $this->check_missing($option, + self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true)); + + if ($minArgs == 0 && $maxArgs == 1) { + # cas particulier: la première valeur doit être collée à l'option + if (count($values) > 0) { + $options[] = "$option=$values[0]"; + $values = array_slice($values, 1); + } else { + $options[] = $option; + } + } else { + $options[] = $option; + } + $options = array_merge($options, $values); + } elseif ($value !== null) { + throw new ArgException("$option: cette option ne prend pas d'arguments"); + } else { + $options[] = $option; + } + + } elseif (substr($arg, 0, 1) === "-") { + ####################################################################### + # option courte + $pos = 1; + $len = strlen($arg); + while ($pos < $len) { + $option = "-".substr($arg, $pos, 1); + $argDef = $this->getArgDef($option); + if ($argDef === null) { + throw new ArgException("$option: option invalide"); + } + if ($argDef->haveArgs) { + $minArgs = $argDef->minArgs; + $maxArgs = $argDef->maxArgs; + $values = []; + if ($len > $pos + 1) { + $values[] = substr($arg, $pos + 1); + $offset = 1; + $pos = $len; + } elseif ($minArgs == 0) { + # cas particulier: la première valeur doit être collée à l'option + # si $maxArgs == 1 + $offset = $maxArgs == 1 ? 1 : 0; + } else { + $offset = 0; + } + $this->check_missing($option, + self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true)); + + if ($minArgs == 0 && $maxArgs == 1) { + # cas particulier: la première valeur doit être collée à l'option + if (count($values) > 0) { + $options[] = "$option$values[0]"; + $values = array_slice($values, 1); + } else { + $options[] = $option; + } + } else { + $options[] = $option; + } + $options = array_merge($options, $values); + } else { + $options[] = $option; + } + $pos++; + } + } else { + #XXX implémenter les commandes + + ####################################################################### + # argument + $remains[] = $arg; + } + } + return array_merge($options, ["--"], $remains); + } + + function process(array $args) { + } +}