rosavox/lib/alveolata/args/functions.php
2025-05-23 07:33:29 +00:00

226 lines
5.1 KiB
PHP

<?php
namespace alveolata\args;
require_once(DIR_ALVEOLATA . '/log/functions.php');
/**
* @param string $pattern
* @param string $subject
* @return array|null
* @author Christian Fraß <frass@greenscale.de>
*/
function _regexp_match(
string $pattern,
string $subject
)
{
$matches = [];
$pattern_ = sprintf('/%s/', $pattern);
$result = preg_match($pattern_, $subject, $matches);
if ($result === 0) {
return null;
}
else {
return array_slice($matches, 1);
}
}
/**
* @param list<§x> $list
* @param function<§x,§y> $function
* @return list<§y>
*/
function _list_map(
array $list,
\Closure $function
) : array
{
$list_ = [];
for ($index = 0; $index < count($list); $index += 1) {
$value = $list[$index];
$value_ = $function($value, $index);
array_push($list_, $value_);
}
return $list_;
}
/**
* @param map<string,§x> $map
* @param function<§x,§y> $function
* @return map<string,§y>
*/
function _map_map(
array $map,
\Closure $function
) : array
{
$map_ = [];
foreach ($map as $key => $value) {
$key_ = $key;
$value_ = $function($value, $key);
$map_[$key_] = $value_;
}
return $map_;
}
/**
* @param record<positioned:list<record<target:string,?default:any,?processing:function<string,any>>>,named:map<string,record<target:string,?default:any,?processing:function<string,any>>>> $spec_raw
* @param list<string> $args_raw
* @return map<string,any>
* @author Christian Fraß <frass@greenscale.de>
*/
function parse(
array $spec_raw,
array $args_raw,
array $settings_given = []
) : array
{
$settings_default = [
'supress_warnings' => false,
];
$settings = array_merge(
$settings_default,
$settings_given
);
// spec refinement
{
$spec = [
'positioned_mandatory' => _list_map(
$spec_raw['positioned_mandatory'],
function ($entry_raw, $index) {
return [
'target' => (
array_key_exists('target', $entry_raw)
? $entry_raw['target']
: sprintf('_arg_%d', $index)
),
'processing' => (
array_key_exists('processing', $entry_raw)
? $entry_raw['processing']
: (function ($value_raw) {return $value_raw;})
),
];
}
),
'positioned_optional' => _list_map(
$spec_raw['positioned_optional'],
function ($entry_raw, $index) use (&$spec_raw) {
return [
'target' => (
array_key_exists('target', $entry_raw)
? $entry_raw['target']
: sprintf('_arg_%d', $index + count($spec_raw['positioned_mandatory']))
),
'processing' => (
array_key_exists('processing', $entry_raw)
? $entry_raw['processing']
: (function ($value_raw) {return $value_raw;})
),
'default' => (
array_key_exists('default', $entry_raw)
? $entry_raw['default']
: null
),
];
}
),
'named' => _map_map(
$spec_raw['named'],
function ($entry_raw, $key) {
return [
'target' => (
array_key_exists('target', $entry_raw)
? $entry_raw['target']
: $key
),
'processing' => (
array_key_exists('processing', $entry_raw)
? $entry_raw['processing']
: (function ($value_raw) {return $value_raw;})
),
'default' => (
array_key_exists('default', $entry_raw)
? $entry_raw['default']
: null
),
];
}
),
];
}
$args = [];
// default values
{
foreach ($spec['positioned_optional'] as $entry) {
$args[$entry['target']] = $entry['default'];
}
foreach ($spec['named'] as $entry) {
$args[$entry['target']] = $entry['default'];
}
}
// parsing
{
$position = 0;
foreach ($args_raw as $arg_raw) {
$result = _regexp_match('--([^=]+)=([^=]+)', $arg_raw);
if ($result === null) {
if ($position < count($spec['positioned_mandatory'])) {
$entry = $spec['positioned_mandatory'][$position];
$value_raw = $entry['processing']($arg_raw);
$value = $value_raw;
$args[$entry['target']] = $value;
}
else if ($position < count($spec['positioned_mandatory']) + count($spec['positioned_optional'])) {
$entry = $spec['positioned_optional'][$position - count($spec['positioned_optional'])];
$value_raw = $entry['processing']($arg_raw);
$value = $value_raw;
$args[$entry['target']] = $value;
}
else {
if (! $settings['supress_warnings']) {
\alveolata\log\warning(
'unrecognized positional argument',
[
'position' => $position,
'value' => $arg_raw,
]
);
}
}
$position += 1;
}
else {
$name = $result[0];
$value_raw = $result[1];
if (array_key_exists($name, $spec['named'])) {
$entry = $spec['named'][$name];
$value = $entry['processing']($value_raw);
$args[$entry['target']] = $value;
}
else {
if (! $settings['supress_warnings']) {
\alveolata\log\warning(
'unrecognized named argument',
[
'name' => $name,
'value' => $value_raw,
]
);
}
}
}
}
$left = array_slice($spec['positioned_mandatory'], $position);
if (count($left) > 0) {
throw (new \Exception(sprintf('%d missing mandatory positioned arguments', count($left))));
}
}
return $args;
}