Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09: - Argument types end to end: :python_cast values are applied (Python @eval receives real bools/numbers/lists), argument values are validated against their argtype patterns with the argtype's description as the error message, argtypes can declare :default (overridable per declaration), and parameterized type families are supported: rest(N) casts a rest argument to an N-dimensional list (bar-count = dimension). - Unified indexed_range syntax (selector with parenthesized subsets, composable mnemonic names) for table lines and spans. - Table klammer: caption fonts fixed in both targets, :column_width / :leading / :colsep wired, :colspan and :rowspan render (HTML attributes; \multicolumn / \multirow), calculated cell values (:calc) with prefix operators, display-precision semantics, :calc_format and :decimal period|comma. - Fonts: closed-world resolution on the Klammertext font store (infrastructure in mac/font_store; no Google Fonts links or fetch). Default fonts live in the top-level fnt/; additional fonts install into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples, preview, install — classification by font metadata). CSS font family names are quoted (digit-initial families were silently lost). - Environment files moved from mac/env/ to the top-level env/; shell profiles source env/runtime.env. Dead per-host variants removed. - Container: fnt/ ships in the image; curl removed (no network use).
This commit is contained in:
@@ -17,16 +17,44 @@ bool operator==(Parameter_set lhs, Parameter_set rhs)
|
||||
|
||||
std::regex parameter_regex(bool optional=false)
|
||||
{
|
||||
//std::string pattern = R"(([A-Za-z]\w*)(?:(\.\w*)(?:(\.\w*)))?)";
|
||||
std::string pattern = R"((?:([A-Za-z]\w*))|(?:([A-Za-z]\w*)\.(\w+))|(?:([A-Za-z]\w*)\.(\w+)\.(\w+)))";
|
||||
// The type component may carry a numeric type parameter, e.g.
|
||||
// rows.rest(2) (see resolve_argtype below). In the optional
|
||||
// three-part form the type may be empty (:paper_size..tex).
|
||||
std::string type = R"(\w+(?:\(\d+\))?)";
|
||||
std::string opt_type = R"(\w*(?:\(\d+\))?)";
|
||||
std::string pattern = R"((?:([A-Za-z]\w*))|(?:([A-Za-z]\w*)\.()" + type +
|
||||
R"())|(?:([A-Za-z]\w*)\.()" + type + R"()\.(\w+)))";
|
||||
if (optional) {
|
||||
pattern = R"((?::([A-Za-z]\w*))|(?::([A-Za-z]\w*)\.(\w+))|(?::([A-Za-z]\w*)\.(\w*)\.(\w+)))";
|
||||
// x x
|
||||
pattern = R"((?::([A-Za-z]\w*))|(?::([A-Za-z]\w*)\.()" + type +
|
||||
R"())|(?::([A-Za-z]\w*)\.()" + opt_type + R"()\.(\w+)))";
|
||||
}
|
||||
// (void)K::log(3, pattern);
|
||||
return std::regex(pattern);
|
||||
}
|
||||
|
||||
// Look up an argument type, specializing a parameterized use such as
|
||||
// rest(2): the base type is copied, its type parameter set, and its
|
||||
// display name extended, so kdesc signatures show rows.rest(2).
|
||||
static Argtype resolve_argtype(
|
||||
const std::string& type_text, const Argtype_set& argtypes, const Locator& loc)
|
||||
{
|
||||
static const std::regex parameterized(R"((\w+)\((\d+)\))");
|
||||
std::smatch match {};
|
||||
if (std::regex_match(type_text, match, parameterized)) {
|
||||
Argtype argtype = argtypes.get(match[1], loc);
|
||||
argtype.m_parameter = match[2];
|
||||
argtype.m_name += "(" + std::string(match[2]) + ")";
|
||||
return argtype;
|
||||
}
|
||||
return argtypes.get(type_text, loc);
|
||||
}
|
||||
|
||||
// True for the rest type in any parameterization (rest, rest(2), ...).
|
||||
static bool is_rest(const Argtype& argtype)
|
||||
{
|
||||
return argtype.m_name == "rest" || argtype.m_name.rfind("rest(", 0) == 0;
|
||||
}
|
||||
|
||||
Parameter_set::Parameter_set(const std::string parameter_string)
|
||||
{
|
||||
(void)K::log(3);
|
||||
@@ -77,7 +105,7 @@ Parameter parse_positional_parameter(const katom_list& katoms, const Argtype_set
|
||||
if (match_type.empty()) {
|
||||
match_type = "string";
|
||||
}
|
||||
return Parameter(match_name, argtypes.get(match_type, k.m_loc), k.m_loc);
|
||||
return Parameter(match_name, resolve_argtype(match_type, argtypes, k.m_loc), k.m_loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +133,19 @@ Parameter parse_optional_parameter(const katom_list& katoms, const Argtype_set&
|
||||
if (match_type.empty()) {
|
||||
match_type = "string";
|
||||
}
|
||||
return Parameter(match_name, argtypes.get(match_type, k.m_loc),
|
||||
k.m_loc, true, default_value);
|
||||
Parameter parameter(match_name, resolve_argtype(match_type, argtypes, k.m_loc),
|
||||
k.m_loc, true, default_value);
|
||||
// Two-level default resolution: a default declared in the
|
||||
// parameter list wins; otherwise the argument type's :default
|
||||
// fills in. Both are validated here, at definition time, so an
|
||||
// invalid default cannot reach an application.
|
||||
if (default_value.empty() && !parameter.m_argtype.m_default.empty()) {
|
||||
parameter.m_default = parameter.m_argtype.m_default;
|
||||
parameter.m_default_from_type = true;
|
||||
} else if (!default_value.empty()) {
|
||||
Parameter_set::validate(parameter, default_value, k.m_loc);
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +252,7 @@ void Parameter_set::parse_parameters(const katom_list& katoms, const Argtype_set
|
||||
auto [positional, optional] = parameter_split(katoms.cbegin(), katoms.cend());
|
||||
for (auto req : positional) {
|
||||
auto pos = parse_positional_parameter(req, argtypes);
|
||||
if (pos.m_argtype.m_name == "rest") {
|
||||
if (is_rest(pos.m_argtype)) {
|
||||
m_rest.push_back(pos);
|
||||
} else {
|
||||
m_positional.push_back(pos);
|
||||
@@ -295,6 +334,13 @@ argument_split(katom_list::const_iterator kbegin, katom_list::const_iterator ken
|
||||
} else if (positional.size() < positional_limit) {
|
||||
positional.push_back(trim_part(p));
|
||||
} else {
|
||||
// Parts were trimmed, so adjacent parts would abut their bar
|
||||
// katoms (a row separator "||" next to an empty cell's "|"
|
||||
// would serialize as "|||"). A space keeps the writer's
|
||||
// bar/double-bar distinction parseable.
|
||||
if (!rest.empty()) {
|
||||
rest.push_back(Katom(" ", katom_t::space, p[0].m_loc));
|
||||
}
|
||||
rest.insert(rest.end(), p.begin(), p.end());
|
||||
}
|
||||
}
|
||||
@@ -386,9 +432,59 @@ Parameter_set::value_map(
|
||||
throw Argument_error(ss.str(), loc);
|
||||
}
|
||||
}
|
||||
for (const auto& [name, value] : values) {
|
||||
const Parameter* parameter = find(name);
|
||||
if (parameter) {
|
||||
validate(*parameter, value, loc);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// Check an argument value against its argument type's pattern. An empty
|
||||
// value (an unsupplied optional argument without a default) is not checked.
|
||||
// The error message includes the argument type's description from its
|
||||
// @@@argtype definition, so the .k description text is what the writer
|
||||
// sees when a complicated value (e.g. a table line specification) is wrong.
|
||||
// The value-size limit guards against std::regex stack overflow: the
|
||||
// libstdc++ executor recurses per character, so a pattern applied to a
|
||||
// very large value crashes. Typed argument values are short; large
|
||||
// values are content (rest, :text) whose types match everything and are
|
||||
// excluded by matches_all() anyway.
|
||||
const size_t validation_size_limit = 4096;
|
||||
|
||||
void Parameter_set::validate(
|
||||
const Parameter& parameter, const std::string& value, const Locator& loc)
|
||||
{
|
||||
if (value.empty() || value.size() > validation_size_limit) {
|
||||
return;
|
||||
}
|
||||
const Argtype& argtype = parameter.m_argtype;
|
||||
if (argtype.matches_all()) {
|
||||
return;
|
||||
}
|
||||
if (!std::regex_match(value, argtype.m_regex)) {
|
||||
std::stringstream ss {};
|
||||
ss << "The value \"" << value << "\" given for the argument \""
|
||||
<< parameter.m_name << "\" does not match the \"" << argtype.m_name
|
||||
<< "\" argument type:\n\n"
|
||||
<< trim(argtype.m_desc) << "\n";
|
||||
throw Argument_error(ss.str(), loc, false);
|
||||
}
|
||||
}
|
||||
|
||||
const Parameter* Parameter_set::find(const std::string& name) const
|
||||
{
|
||||
for (const auto& params : {&m_positional, &m_optional, &m_rest}) {
|
||||
for (const Parameter& p : *params) {
|
||||
if (p.m_name == name) {
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parameter/argument substitution
|
||||
|
||||
std::string replace_arguments(
|
||||
|
||||
Reference in New Issue
Block a user