Table of Contents

Interpreter Generator

The Interpreter generator turns annotated rule methods into an immutable Interpreter<TContext, TResult> factory. Use it when a DSL grammar is stable enough to describe in code and you want compile-time diagnostics, deterministic registration order, and a factory that can be imported through IServiceCollection.

Quick Start

using PatternKit.Generators.Interpreter;

[GenerateInterpreter(typeof(PricingContext), typeof(decimal), FactoryMethodName = "Build")]
public static partial class PricingRules
{
    [InterpreterTerminal("number")]
    private static decimal Number(string token) => decimal.Parse(token);

    [InterpreterTerminal("cart_total")]
    private static decimal CartTotal(string token, PricingContext context) => context.CartTotal;

    [InterpreterNonTerminal("add")]
    private static decimal Add(decimal[] args) => args[0] + args[1];

    [InterpreterNonTerminal("round")]
    private static decimal Round(decimal[] args) => Math.Round(args[0], 2);
}

var interpreter = PricingRules.Build();

The generated method composes the existing fluent runtime:

var builder = Interpreter.Create<PricingContext, decimal>();
builder.Terminal("number", static (token, context) => Number(token));
builder.Terminal("cart_total", static (token, context) => CartTotal(token, context));
builder.NonTerminal("add", static (args, context) => Add(args));
builder.NonTerminal("round", static (args, context) => Round(args));
return builder.Build();

Attribute Model

Attribute Target Purpose
[GenerateInterpreter(typeof(TContext), typeof(TResult))] partial class or struct Generates an Interpreter<TContext, TResult> factory.
[InterpreterTerminal("name")] static method Registers a terminal handler for token values.
[InterpreterNonTerminal("name")] static method Registers a non-terminal handler for child results.

Valid Rule Signatures

Terminal handlers return TResult and accept either:

static TResult Rule(string token)
static TResult Rule(string token, TContext context)

Non-terminal handlers return TResult and accept either:

static TResult Rule(TResult[] args)
static TResult Rule(TResult[] args, TContext context)

Rules can be private because the generated factory is emitted into the same partial type.

Diagnostics

Id Meaning
PKINT001 The host type is marked with [GenerateInterpreter] but is not partial.
PKINT002 The host has no terminal or non-terminal rules.
PKINT003 A rule method is not static, returns the wrong type, or has an invalid parameter list.
PKINT004 A terminal or non-terminal rule name is registered more than once.

IServiceCollection Integration

Register the generated interpreter as a singleton. The interpreter is immutable after build and can be safely shared by request handlers, hosted services, ASP.NET Core endpoints, and background workers.

public static IServiceCollection AddPricingRules(this IServiceCollection services)
{
    services.AddSingleton(_ => PricingRules.Build());
    return services;
}

PatternKit's e-commerce Interpreter example exposes both fluent and generated pricing/eligibility interpreters and registers the generated path through AddPatternKitExamples.

See Also