An optional argument has three values: the default (the name is absent), the argument type's :alone value (the name is written alone), and a written value. :alone is declared by the argument type only, never by a klammer's parameter declaration -- a default is what one klammer means by silence, but a bare option name must read the same way in every klammer. The bool type declares :alone true, which is the whole of the convention that a bare boolean option means true; there is no boolean special case in the engine. A type whose pattern matches running text cannot declare :alone, since an option's value runs to the next bar or option name and would swallow the following text. kdesc and `ktext -m` now show [default: X] and [alone: Y] per argument type. In the Standard Klammer Set: @code :number becomes a bool (it was an untyped string tested only for truthiness, so a bare :number was a no-op); decimal_mark declares :alone comma; table_hline and table_vline declare :alone all. The 35 bools that default false gained the bare form for free. Tests: tst/alone_test.sh (21 cases) joins the shipped suite. (from dev 6024f49c2859)
122 lines
3.5 KiB
C++
122 lines
3.5 KiB
C++
#include <set>
|
|
#include <regex>
|
|
#include <sstream>
|
|
|
|
#include "argtype.h"
|
|
#include "error.h"
|
|
#include "util.h"
|
|
|
|
Argtype::Argtype(std::string name, std::string desc, std::string symbolic_pattern, std::string pattern,
|
|
std::string default_value, std::string alone_value,
|
|
std::string python_cast, modify_string_f python_format,
|
|
const Locator& loc)
|
|
: m_name(name)
|
|
, m_desc(desc)
|
|
, m_symbolic_pattern(symbolic_pattern)
|
|
, m_pattern(pattern)
|
|
, m_default(default_value)
|
|
, m_alone(alone_value)
|
|
, m_python_cast(python_cast)
|
|
, m_python_format(python_format)
|
|
, m_regex(std::regex(pattern))
|
|
, m_loc(loc)
|
|
{
|
|
}
|
|
|
|
static bool empty_value(const strings_t& v)
|
|
{
|
|
return v.empty() || (v.size() == 1 && v[0].empty());
|
|
}
|
|
|
|
std::string pyformat_string(const strings_t& v)
|
|
{
|
|
std::string p = v.empty() ? "" : v[0];
|
|
p = string_replace(p, "\\", "\\\\");
|
|
p = string_replace(p, "\"", "\\\"");
|
|
p = string_replace(p, "\n", "\\n");
|
|
return "\"" + p + "\"";
|
|
}
|
|
|
|
std::string pyformat_bool(const strings_t& v)
|
|
{
|
|
if (empty_value(v)) {
|
|
return "None";
|
|
}
|
|
std::string value = v[0];
|
|
std::set<std::string> true_values { "1", "true", "True", "yes" };
|
|
std::set<std::string> false_values { "0", "false", "False", "no" };
|
|
if (true_values.contains(value)) {
|
|
return "True";
|
|
} else if (false_values.contains(value)) {
|
|
return "False";
|
|
} else {
|
|
throw Argument_error("The argument\"" + value + "\" is not a Boolean value");
|
|
}
|
|
}
|
|
|
|
std::string pyformat_number(const strings_t& value)
|
|
{
|
|
if (empty_value(value)) {
|
|
return "None";
|
|
}
|
|
return value[0];
|
|
}
|
|
|
|
std::string pylist(strings_t words)
|
|
{
|
|
std::transform(words.begin(), words.end(), words.begin(),
|
|
[](const std::string& s) { return pyformat_string({s}); });
|
|
return "[" + join(words, ", ") + "]";
|
|
}
|
|
|
|
std::string pyformat_list(const strings_t& value)
|
|
{
|
|
strings_t words {};
|
|
for (const std::string& v : value) {
|
|
for (const std::string& word : word_split(v)) {
|
|
if (!word.empty()) {
|
|
words.push_back(word);
|
|
}
|
|
}
|
|
}
|
|
return pylist(words);
|
|
}
|
|
|
|
std::string pyformat_dlist(const strings_t& value)
|
|
{
|
|
strings_t items {};
|
|
for (const std::string& v : value) {
|
|
if (v.empty()) {
|
|
continue;
|
|
}
|
|
for (const std::string& item : dlist_split(v)) {
|
|
items.push_back(item);
|
|
}
|
|
}
|
|
return pylist(items);
|
|
}
|
|
|
|
std::string Argtype::python_value(const std::string& var_name, std::vector<std::string> value, size_t name_size)
|
|
{
|
|
std::stringstream ss {};
|
|
ss << " " << std::left << std::setw(name_size) << var_name << " = ";
|
|
if (m_python_format) {
|
|
ss << m_python_format(value);
|
|
} else if (m_python_cast.empty() || m_python_cast == "str") {
|
|
// String-family types (and untyped variables): plain quoted string.
|
|
ss << pyformat_string(value);
|
|
} else if (!m_parameter.empty()) {
|
|
// Parameterized type: bind the type parameter as N around the
|
|
// cast, e.g. (lambda N: <cast>)(2)("A | B || C | D").
|
|
ss << "(lambda N: " << m_python_cast << ")(" << m_parameter << ")("
|
|
<< pyformat_string(value) << ")";
|
|
} else {
|
|
// User-defined :python_cast expression, applied to the raw value.
|
|
// The cast is applied to an empty value too, so e.g. a split lambda
|
|
// yields [] for an unsupplied argument.
|
|
ss << m_python_cast << "(" << pyformat_string(value) << ")";
|
|
}
|
|
return ss.str();
|
|
}
|
|
|