632a227985
We no longer do intrin specs as a separate stimbuff; rather now we do them as a specifier segment within the pipeline spec of a normal nontrin spec.
279 lines
7.2 KiB
C++
279 lines
7.2 KiB
C++
#ifndef SMO_INTRIN_THRESHOLD_PARAMS_H
|
|
#define SMO_INTRIN_THRESHOLD_PARAMS_H
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace smo::intrin {
|
|
|
|
enum class ThresholdUnit
|
|
{
|
|
Percentage,
|
|
Absolute,
|
|
};
|
|
|
|
struct ParsedThresholdParam
|
|
{
|
|
int value;
|
|
ThresholdUnit unit;
|
|
std::string_view matchedName;
|
|
bool wasSpecified;
|
|
};
|
|
|
|
/* Canonical unprefixed threshold-param names that live inside postrin(...) /
|
|
* negtrin(...) segments attached to a nontrin DAP spec. The "-pc" and
|
|
* "-percentage" variants are Percentage-unit; the "-thr", "-thresh" and
|
|
* "-threshold" variants are Absolute-unit.
|
|
*/
|
|
inline constexpr std::array<std::string_view, 2> kIntrinInterestPcNames = {
|
|
"interest-percentage",
|
|
"interest-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kIntrinInterestThrNames = {
|
|
"interest-threshold",
|
|
"interest-thresh",
|
|
"interest-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kIntrinDistractionPcNames = {
|
|
"distraction-percentage",
|
|
"distraction-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kIntrinDistractionThrNames = {
|
|
"distraction-threshold",
|
|
"distraction-thresh",
|
|
"distraction-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 4> kIntrinStupefactionPcNames = {
|
|
"stupefaction-percentage",
|
|
"stupefaction-pc",
|
|
"stupefying-percentage",
|
|
"stupefying-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 6> kIntrinStupefactionThrNames = {
|
|
"stupefaction-threshold",
|
|
"stupefaction-thresh",
|
|
"stupefaction-thr",
|
|
"stupefying-threshold",
|
|
"stupefying-thresh",
|
|
"stupefying-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kIntrinIntolerablePcNames = {
|
|
"intolerable-percentage",
|
|
"intolerable-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kIntrinIntolerableThrNames = {
|
|
"intolerable-threshold",
|
|
"intolerable-thresh",
|
|
"intolerable-thr",
|
|
};
|
|
|
|
/* Unitless stems are invalid — authors must name the unit via -pc/-percentage
|
|
* or -thr/-thresh/-threshold. We reject bare "interest", "distraction", etc.
|
|
*/
|
|
inline constexpr std::array<std::string_view, 5> kForbiddenUnitlessIntrinStems = {
|
|
"interest",
|
|
"distraction",
|
|
"stupefaction",
|
|
"stupefying",
|
|
"intolerable",
|
|
};
|
|
|
|
template <std::size_t N>
|
|
inline bool arrayContains(
|
|
const std::array<std::string_view, N>& names,
|
|
std::string_view candidate)
|
|
{
|
|
for (const auto& name : names) {
|
|
if (name == candidate) { return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename NameCollectionT>
|
|
inline bool namesContain(
|
|
const NameCollectionT& names,
|
|
std::string_view candidate)
|
|
{
|
|
for (const auto& name : names) {
|
|
if (name == candidate) { return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool isKnownIntrinThresholdParamName(std::string_view name)
|
|
{
|
|
return namesContain(kIntrinInterestPcNames, name)
|
|
|| namesContain(kIntrinInterestThrNames, name)
|
|
|| namesContain(kIntrinDistractionPcNames, name)
|
|
|| namesContain(kIntrinDistractionThrNames, name)
|
|
|| namesContain(kIntrinStupefactionPcNames, name)
|
|
|| namesContain(kIntrinStupefactionThrNames, name)
|
|
|| namesContain(kIntrinIntolerablePcNames, name)
|
|
|| namesContain(kIntrinIntolerableThrNames, name);
|
|
}
|
|
|
|
/* Intrin threshold params (interest-*, distraction-*, stupefaction-*,
|
|
* intolerable-*) must appear only inside postrin(...) / negtrin(...) segments
|
|
* on a DAP spec, never on qualeIfaceApi params. The deprecated `from-stimbuff`
|
|
* marker is also rejected — postrin/negtrin are now directly attached to the
|
|
* nontrin stimbuff they trigger from.
|
|
*/
|
|
inline void validateNoIntrinParamsOnQualeIface(
|
|
const std::string& qualeIfaceApi,
|
|
const std::vector<std::pair<std::string, std::string>>& params)
|
|
{
|
|
for (const auto& [name, value] : params)
|
|
{
|
|
(void)value;
|
|
|
|
if (isKnownIntrinThresholdParamName(name))
|
|
{
|
|
throw std::runtime_error(
|
|
"Intrinsic threshold param '" + name + "' is not valid on "
|
|
"qualeIfaceApi '" + qualeIfaceApi + "'. Declare it inside a "
|
|
"postrin(...) or negtrin(...) segment attached to this DAP "
|
|
"spec.");
|
|
}
|
|
|
|
if (arrayContains(kForbiddenUnitlessIntrinStems, name))
|
|
{
|
|
throw std::runtime_error(
|
|
"Intrinsic threshold param '" + name + "' on qualeIfaceApi '"
|
|
+ qualeIfaceApi + "' is invalid without a unit suffix and "
|
|
"does not belong on qualeIfaceApi params anyway. Use a "
|
|
"postrin(...) or negtrin(...) segment with a '-pc' or "
|
|
"'-thr' suffix.");
|
|
}
|
|
|
|
if (name == "from-stimbuff")
|
|
{
|
|
throw std::runtime_error(
|
|
"'from-stimbuff' is no longer supported. postrin(...) and "
|
|
"negtrin(...) are now attached directly to the nontrin DAP "
|
|
"spec they trigger from; remove 'from-stimbuff' from "
|
|
"qualeIfaceApi '" + qualeIfaceApi + "'.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Accepts only unprefixed threshold names and the two passband modifiers that
|
|
* are defined on the sensory side. Rejects unit-less stems so that mis-typed
|
|
* params fail loudly.
|
|
*/
|
|
inline void validateIntrinSegmentParams(
|
|
std::string_view intrinKind, // "postrin" or "negtrin"
|
|
const std::vector<std::pair<std::string, std::string>>& params)
|
|
{
|
|
for (const auto& [name, value] : params)
|
|
{
|
|
(void)value;
|
|
|
|
if (arrayContains(kForbiddenUnitlessIntrinStems, name))
|
|
{
|
|
throw std::runtime_error(
|
|
std::string(intrinKind) + "(...) param '" + name + "' is "
|
|
"invalid without a unit suffix. Use '-pc'/'-percentage' or "
|
|
"'-thr'/'-thresh'/'-threshold'.");
|
|
}
|
|
}
|
|
}
|
|
|
|
inline std::optional<std::pair<std::string_view, int>> findLastMatchingIntParam(
|
|
const std::vector<std::pair<std::string, std::string>>& params,
|
|
const auto& synonymNames)
|
|
{
|
|
for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt)
|
|
{
|
|
const auto& [name, value] = *paramIt;
|
|
if (!namesContain(synonymNames, name))
|
|
{ continue; }
|
|
|
|
try {
|
|
return std::pair<std::string_view, int>(name, std::stoi(value));
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error(
|
|
"Failed to parse '" + name + "' param value '" + value
|
|
+ "' as integer: " + e.what());
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
inline ParsedThresholdParam parseOptionalThresholdParam(
|
|
const std::vector<std::pair<std::string, std::string>>& params,
|
|
const auto& percentageNames,
|
|
const auto& absoluteNames,
|
|
int defaultValue,
|
|
ThresholdUnit defaultUnit)
|
|
{
|
|
for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt)
|
|
{
|
|
const auto& [name, value] = *paramIt;
|
|
ThresholdUnit unit;
|
|
|
|
if (namesContain(percentageNames, name))
|
|
{ unit = ThresholdUnit::Percentage; }
|
|
else if (namesContain(absoluteNames, name))
|
|
{ unit = ThresholdUnit::Absolute; }
|
|
else
|
|
{ continue; }
|
|
|
|
try
|
|
{
|
|
return ParsedThresholdParam{
|
|
.value = std::stoi(value),
|
|
.unit = unit,
|
|
.matchedName = name,
|
|
.wasSpecified = true,
|
|
};
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error(
|
|
"Failed to parse '" + name + "' param value '" + value
|
|
+ "' as integer: " + e.what());
|
|
}
|
|
}
|
|
|
|
return ParsedThresholdParam{
|
|
.value = defaultValue,
|
|
.unit = defaultUnit,
|
|
.matchedName = {},
|
|
.wasSpecified = false,
|
|
};
|
|
}
|
|
|
|
inline uint32_t resolveThresholdValue(
|
|
const ParsedThresholdParam& param,
|
|
size_t percentageBase)
|
|
{
|
|
if (param.unit == ThresholdUnit::Percentage) {
|
|
return static_cast<uint32_t>((percentageBase * param.value) / 100);
|
|
}
|
|
|
|
return static_cast<uint32_t>(param.value);
|
|
}
|
|
|
|
} // namespace smo::intrin
|
|
|
|
#endif // SMO_INTRIN_THRESHOLD_PARAMS_H
|