Skip to main content

Creating Methods And Chain Features

One of the first real plugin-author tasks is adding a new method or chain method to an existing Morph-owned feature.

This is already how the repo models things such as:

  • Input<T>.Min(...)
  • Input<T>.Max(...)
  • Input<T>.Default(...)
  • Tensor.Random(...)

A Real Manifest Example

From the current Core package, a chain method is declared like this:

[core.input.chain.min]
method = "Min"
source = "features/Input/chain/Min.cpp"
component_class = "core.input.chain.min"
param_minValue = ["t"]
returns = "Self"

This is not just documentation.

It tells the framework:

  • the method name exposed to the language
  • the source file that implements the semantic chain behavior
  • the component class that generated glue and registries will resolve
  • the parameter contract
  • the return contract

Another Real Example

From the Tensor package:

[tensor.random.chain.random]
method = "Random"
source = "features/Tensor/chain/Random.cpp"
component_class = "tensor.chain.random"
param_rows = ["int"]
param_cols = ["int"]
returns = "Self"

This is how a package-owned method gets declared without editing a core method table.


The Implementation Side

The matching implementation for Input<T>.Min(...) lives in package space:

#include "../InputFeaturePlugin.h"

namespace morph::nir::morph {
namespace {

using morph::morph::ChainSemanticFeature;
using morph::morph::ChainSignatureView;
using morph::morph::SemanticFeatureContext;
using morph::morph::FeatureSegmentView;
using core_input::InputFeatureSemanticFact;

class InputMinChainSemantic final : public ChainSemanticFeature {
public:
bool apply(SemanticFeatureContext &ctx,
const morph::morph::FeatureInvocationView &,
const FeatureSegmentView &segment,
const ChainSignatureView &signature,
morph::morph::FeatureSemanticFact &fact,
std::string *outError) override {
InputFeatureSemanticFact *inputFact =
core_input::asInputFeatureSemanticFact(fact);
if (inputFact == nullptr || inputFact->inputType == nullptr) {
if (outError != nullptr) {
*outError =
"Input<T>.Min() received an incompatible featureInvocation fact.";
}
return false;
}
if (!inputFact->inputType->isNumeric()) {
if (outError != nullptr) {
*outError = "Input<T>.Min() is only valid for numeric T.";
}
return false;
}
if (!core_input::expectChainArity(segment, signature, outError)) {
return false;
}

ASTNode *argNode = core_input::singleArgument(segment);
NTypePtr argType = ctx.infer(argNode);
if (argType != nullptr && !argType->isError() && !argType->isUnknown() &&
!argType->isDynamic() && !argType->isNumeric()) {
if (outError != nullptr) {
*outError = "Input<T>.Min() argument must be numeric.";
}
return false;
}

inputFact->minArg = argNode;
return true;
}
};

} // namespace

MORPHLANG_MORPH_DEFINE_CHAIN_SEMANTIC_FACTORY(core_input_chain_min,
InputMinChainSemantic)
} // namespace morph::nir::morph

What This Code Is Doing

This file shows the real pattern:

  1. inherit from ChainSemanticFeature
  2. validate the incoming fact type
  3. validate the receiver state
  4. validate arity and argument types
  5. write accepted information back into the feature fact
  6. export the factory with a Morph SDK macro

Nothing here requires a core semantic switch table.


How To Add A New Method

If you want to add a new chain method:

  1. choose the owning package
  2. add a new section to the package feature manifest
  3. implement a package-local semantic class like the example above
  4. export it with the matching factory macro
  5. update tests for arity, typing, and valid usage

The package owns the method from manifest to implementation.


What Not To Do

Do not:

  • patch a core method map
  • add one-off method parsing to generic parser code
  • encode package-specific method behavior in unrelated sema files

The method belongs to the package feature graph.


Next Steps