Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
59ba170
Klee: Adds callback functionality to lua-based Klee input
kennyweiss Jun 30, 2026
2b84e46
Klee: Adds ability to load variables into klee lua
kennyweiss Jun 30, 2026
a76830f
Klee: Adds LuaBindingChunk as a way of passing state to the lua Klee …
kennyweiss Jul 1, 2026
55d43a2
Improves how we override inlet checks for functions
kennyweiss Jul 9, 2026
e99d25f
Only load the shapeset once in the tutorial example
kennyweiss Jul 9, 2026
0459b14
inlet: keep Lua state alive for returned functions
kennyweiss Jul 9, 2026
2d427b0
klee: Improves docs and tests about input lua variables to klee deck
kennyweiss Jul 9, 2026
987b8c2
klee: Improves test that compares lua and yaml parsing
kennyweiss Jul 9, 2026
f0a3035
docs: demonstrate callback-based Klee Lua tutorial deck
kennyweiss Jul 9, 2026
be06403
Klee: several cleanups to callback support
kennyweiss Jul 29, 2026
725ed5c
Klee: Isolates Lua bindings environment
kennyweiss Jul 29, 2026
016c46c
Klee: Streamlines variables/bindings for Lua input into single struct
kennyweiss Jul 29, 2026
404fbf3
Klee: Improved validation of user input -- checks for lua keywords
kennyweiss Jul 29, 2026
0f81df2
Reworks callback registration in Inlet/Klee
kennyweiss Jul 29, 2026
ed6a381
Klee: Improves internal plumbing of Lua callbacks
kennyweiss Jul 29, 2026
f845e3f
Inlet: Improves path-finding for nested callback aliases
kennyweiss Jul 30, 2026
17b5e9d
Inlet: Adds addFunctionAsValueAlternative to support cases that benef…
kennyweiss Jul 30, 2026
fb27576
Klee: Stabilized Lua runtime input contract
kennyweiss Jul 30, 2026
70c8fc5
Inlet: simplifies LuaReader's variant map and index retrieval
kennyweiss Jul 30, 2026
4adf1e9
Inlet: Adds InputPath descriptor for a path to indicate if it is abso…
kennyweiss Jul 30, 2026
57f1f67
Inlet: Improves tests coverage and docs for addFunctionAsValueAlterna…
kennyweiss Jul 30, 2026
e2655c5
Inlet: Adds automatically named functions to inlet and container
kennyweiss Jul 30, 2026
3f32257
Klee: Ensure callbacks work for named operators
kennyweiss Jul 30, 2026
d47bf96
Klee: Adds tests for callbacks on operators
kennyweiss Jul 30, 2026
bfb0709
Integrates Lua callbacks into quest shaping example and tutorial
kennyweiss Aug 8, 2026
b20c615
Updates data submodule with new shaping example
kennyweiss Aug 8, 2026
992cfd5
Inlet: Defines restart behavior for function alternatives
kennyweiss Aug 9, 2026
828936f
Inlet: Improve docs and examples related to lua functions
kennyweiss Aug 9, 2026
6d31e04
Light touchup to docs for consistency
kennyweiss Aug 9, 2026
6799395
Klee: Improves error messages related to Slice callbacks
kennyweiss Aug 9, 2026
3491517
Inlet: Simplifies changes to support callbacks as value alternates
kennyweiss Aug 10, 2026
7f06c0b
Adds missing doxygen to new/changed functions
kennyweiss Aug 10, 2026
3a6f114
Inlet: Function value alternatives now own their FunctionVariant call…
kennyweiss Aug 10, 2026
2794a91
Inlet; Adds a single canonical path for handling functions as alterna…
kennyweiss Aug 10, 2026
efa90db
Inlet: Cleanup -- removes unnecessary traversal function
kennyweiss Aug 10, 2026
0183ea4
Inlet: Consolidates/streamlines new inlet tests
kennyweiss Aug 10, 2026
1f78feb
Inlet: Streamlines new user and developer docs
kennyweiss Aug 10, 2026
d6cbb4e
Minor fixup for formatting and a return variable
kennyweiss Aug 11, 2026
0f25993
Inlet: Simplifies addFunctionAsValueAlternative by requiring it to be…
kennyweiss Aug 11, 2026
a778933
Klee: Consistently declares callbacks before data for operators
kennyweiss Aug 11, 2026
dc6e9ed
Klee: Removes SingleOperatorData -- it was an unnecessary abstraction
kennyweiss Aug 11, 2026
d773826
Klee: Simplifies error/exception messages in GeometryOperatorsIO
kennyweiss Aug 11, 2026
a26457a
Klee: Simplifies Lua initialization to Klee input
kennyweiss Aug 11, 2026
4abd283
Klee: Fixes transposed parameters in an error message and improves re…
kennyweiss Aug 11, 2026
9dbd5e4
Klee: Documents processing order for Klee operators and shapes
kennyweiss Aug 11, 2026
1bd1c0b
Bugfix for bump dependency in mesh clipper
kennyweiss Aug 11, 2026
df9d446
Inlet: Use InletError instead of std::runtime_error for errors during…
kennyweiss Aug 11, 2026
d313f57
Inlet: Add error message if setting function alternative after settin…
kennyweiss Aug 11, 2026
740799f
Klee: Use string_view to avoid warning
kennyweiss Aug 11, 2026
5a84b81
Klee: Removes redundant io tests
kennyweiss Aug 11, 2026
bc2bad0
Avoids race condition in shaping tutorial tests when writing shaping.…
kennyweiss Aug 25, 2026
b3663e1
Formats code
Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 134 additions & 26 deletions src/axom/inlet/Container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,16 @@ Field& Container::addField(axom::sidre::Group* sidreGroup,
return *(emplace_result.first->second);
}

Function& Container::addFunctionInternal(axom::sidre::Group* sidreGroup,
FunctionVariant&& func,
const std::string& fullName,
const std::string& name)
Function& Container::storeFunction(axom::sidre::Group* sidreGroup,
FunctionVariant&& func,
const std::string& fullName,
const std::string& name)
{
const size_t found = name.find_last_of("/");
auto currContainer = this;
if(found != std::string::npos)
{
// This will add any intermediate Containers (if not present) before adding the field
// This will add any intermediate Containers (if not present) before storing the function
currContainer = &addContainer(name.substr(0, found));
}
const auto& emplace_result = currContainer->m_functionChildren.emplace(
Expand Down Expand Up @@ -376,7 +376,8 @@ VerifiableScalar& Container::addPrimitive(const std::string& name,
lookupPath =
utilities::string::removeAllInstances(lookupPath, detail::COLLECTION_GROUP_NAME + "/");
detail::updateUnexpectedNames(lookupPath, m_unexpectedNames);
auto typeId = addPrimitiveHelper(sidreGroup, lookupPath, forArray, val);
auto typeId =
addPrimitiveHelper(sidreGroup, lookupPath, forArray, val, containsFunctionValueAlternative(name));
return addField(sidreGroup, typeId, fullName, name);
}
}
Expand All @@ -385,9 +386,16 @@ template <>
axom::sidre::DataTypeId Container::addPrimitiveHelper<bool>(axom::sidre::Group* sidreGroup,
const std::string& lookupPath,
bool forArray,
bool val)
bool val,
bool hasFunctionAlternative)
{
const auto result = m_reader.getBool(lookupPath, val);
auto result = m_reader.getBool(lookupPath, val);
if(hasFunctionAlternative && result == ReaderResult::WrongType)
{
// The input supplied the function representation of this value, so the
// concrete entry is absent rather than of the wrong type.
result = ReaderResult::NotFound;
}
if(forArray || result == ReaderResult::Success)
{
sidreGroup->createViewScalar("value", val ? std::int8_t(1) : std::int8_t(0));
Expand All @@ -403,9 +411,16 @@ template <>
axom::sidre::DataTypeId Container::addPrimitiveHelper<int>(axom::sidre::Group* sidreGroup,
const std::string& lookupPath,
bool forArray,
int val)
int val,
bool hasFunctionAlternative)
{
const auto result = m_reader.getInt(lookupPath, val);
auto result = m_reader.getInt(lookupPath, val);
if(hasFunctionAlternative && result == ReaderResult::WrongType)
{
// The input supplied the function representation of this value, so the
// concrete entry is absent rather than of the wrong type.
result = ReaderResult::NotFound;
}
if(forArray || result == ReaderResult::Success)
{
sidreGroup->createViewScalar("value", val);
Expand All @@ -421,9 +436,16 @@ template <>
axom::sidre::DataTypeId Container::addPrimitiveHelper<double>(axom::sidre::Group* sidreGroup,
const std::string& lookupPath,
bool forArray,
double val)
double val,
bool hasFunctionAlternative)
{
const auto result = m_reader.getDouble(lookupPath, val);
auto result = m_reader.getDouble(lookupPath, val);
if(hasFunctionAlternative && result == ReaderResult::WrongType)
{
// The input supplied the function representation of this value, so the
// concrete entry is absent rather than of the wrong type.
result = ReaderResult::NotFound;
}
if(forArray || result == ReaderResult::Success)
{
sidreGroup->createViewScalar("value", val);
Expand All @@ -439,9 +461,16 @@ template <>
axom::sidre::DataTypeId Container::addPrimitiveHelper<std::string>(axom::sidre::Group* sidreGroup,
const std::string& lookupPath,
bool forArray,
std::string val)
std::string val,
bool hasFunctionAlternative)
{
const auto result = m_reader.getString(lookupPath, val);
auto result = m_reader.getString(lookupPath, val);
if(hasFunctionAlternative && result == ReaderResult::WrongType)
{
// The input supplied the function representation of this value,
// so the concrete entry is absent rather than of the wrong type.
result = ReaderResult::NotFound;
}
if(forArray || result == ReaderResult::Success)
{
sidreGroup->createViewString("value", val);
Expand Down Expand Up @@ -854,7 +883,13 @@ Verifiable<Container>& Container::addPrimitiveArray(const std::string& name,
utilities::string::removeAllInstances(lookupPath, detail::COLLECTION_GROUP_NAME + "/");
detail::updateUnexpectedNames(lookupPath, m_unexpectedNames);
std::vector<VariantKey> indices;
if(isDict)
if(containsFunctionValueAlternative(name))
{
// The input supplied the function representation of this collection,
// so the concrete entry is absent rather than of the wrong type.
markRetrievalStatus(*container.sidreGroup(), ReaderResult::NotFound);
}
else if(isDict)
{
indices = detail::PrimitiveArrayHelper<VariantKey, T>::add(container, m_reader, lookupPath);
}
Expand Down Expand Up @@ -897,17 +932,54 @@ Verifiable<Function>& Container::addFunction(const std::string& name,
const std::string& description,
const std::string& pathOverride)
{
// If it has indices, we're adding a function to an array
// of structs, so we need to iterate over the subcontainers
// corresponding to elements of the array
return addFunctionInternal(name, name, ret_type, arg_types, description, pathOverride);
}

Verifiable<Function>& Container::addFunctionAsValueAlternative(const std::string& valueName,
const FunctionTag ret_type,
const std::vector<FunctionTag>& arg_types,
const std::string& description)
{
SLIC_ERROR_IF(valueName.empty(),
"[Inlet] A function value alternative requires a non-empty value name");
SLIC_ERROR_IF(ret_type == FunctionTag::Void,
"[Inlet] A function value alternative requires a non-void return type");
// Declaring the concrete entry first would already have marked it as being of
// the wrong type, which surfaces later as a confusing verification failure
// blaming the input rather than the schema. Reject it here instead.
SLIC_ERROR_IF(
getChildInternal<Field>(valueName) != nullptr || getChildInternal<Container>(valueName) != nullptr,
fmt::format("[Inlet] The function value alternative for '{0}' must be declared before '{0}'",
valueName));
// The alternative is read from the concrete value's input path but is stored
// under a distinct schema name so the two entries do not collide.
return addFunctionInternal(detail::functionAlternativeName(valueName),
valueName,
ret_type,
arg_types,
description,
"");
}

Verifiable<Function>& Container::addFunctionInternal(const std::string& schemaName,
const std::string& inputName,
const FunctionTag ret_type,
const std::vector<FunctionTag>& arg_types,
const std::string& description,
const std::string& pathOverride)
{
// If it has indices, we're adding a function to an array of structs,
// so we need to iterate over the subcontainers corresponding to elements of the array
std::vector<std::reference_wrapper<Verifiable<Function>>> funcs;

const bool is_nested = transformFromNestedElements(
std::back_inserter(funcs),
name,
[&name, &ret_type, &arg_types, &description](Container& subcontainer,
const std::string& path) -> Verifiable<Function>& {
return subcontainer.addFunction(name, ret_type, arg_types, description, path);
inputName,
[&schemaName, &inputName, &ret_type, &arg_types, &description](
Container& subcontainer,
const std::string& path) -> Verifiable<Function>& {
return subcontainer
.addFunctionInternal(schemaName, inputName, ret_type, arg_types, description, path);
});
if(is_nested)
{
Expand All @@ -921,7 +993,7 @@ Verifiable<Function>& Container::addFunction(const std::string& name,
else
{
// Otherwise actually add a Function
std::string fullName = utilities::string::appendPrefix(m_name, name);
std::string fullName = utilities::string::appendPrefix(m_name, schemaName);
// First check if the function already exists
auto iter = m_functionChildren.find(fullName);
if(iter != m_functionChildren.end())
Expand All @@ -933,13 +1005,14 @@ Verifiable<Function>& Container::addFunction(const std::string& name,
fmt::format("Failed to create Sidre group with name '{0}'", fullName));
detail::addSignatureToGroup(ret_type, arg_types, sidreGroup);
// If a pathOverride is specified, needed when Inlet-internal groups
// are part of fullName
std::string lookupPath = (pathOverride.empty()) ? fullName : pathOverride;
// are part of the input path
std::string lookupPath =
(pathOverride.empty()) ? utilities::string::appendPrefix(m_name, inputName) : pathOverride;
lookupPath =
utilities::string::removeAllInstances(lookupPath, detail::COLLECTION_GROUP_NAME + "/");
detail::updateUnexpectedNames(lookupPath, m_unexpectedNames);
auto func = m_reader.getFunction(lookupPath, ret_type, arg_types);
return addFunctionInternal(sidreGroup, std::move(func), fullName, name);
return storeFunction(sidreGroup, std::move(func), fullName, schemaName);
}
}

Expand Down Expand Up @@ -1303,6 +1376,13 @@ bool Container::isUserProvided() const

bool Container::isUserProvided(const std::string& name) const
{
// A function value alternative is stored under a different schema name than
// the value it applies to, so it is not found by the child lookups below
if(containsFunctionValueAlternative(name))
{
return true;
}

if(auto container = getChildInternal<Container>(name))
{
// Check if the container itself was provided by the user
Expand Down Expand Up @@ -1336,5 +1416,33 @@ const std::unordered_map<std::string, std::unique_ptr<Function>>& Container::get
return m_functionChildren;
}

bool Container::containsFunctionValueAlternative(const std::string& valueName) const
{
auto function = getChildInternal<Function>(detail::functionAlternativeName(valueName));
return function != nullptr && static_cast<bool>(*function);
}

const Function& Container::getFunctionValueAlternative(const std::string& valueName) const
{
return getFunction(detail::functionAlternativeName(valueName));
}

std::vector<std::string> Container::getFunctionValueAlternativeNames() const
{
std::vector<std::string> names;
for(const auto& entry : m_functionChildren)
{
const std::string childName = Path(entry.first).baseName();
if(detail::isFunctionAlternativeName(childName) && static_cast<bool>(*entry.second))
{
names.push_back(
childName.substr(0, childName.size() - detail::FUNCTION_ALTERNATIVE_SUFFIX.size()));
}
}
// m_functionChildren is unordered, so sort for a reproducible result
std::sort(names.begin(), names.end());
return names;
}

} // namespace inlet
} // namespace axom
Loading