226 lines
5.1 KiB
PHP
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;
|
|
}
|
|
|
|
|