Skip to content

MagicAST restructure: Phase 3 — Parser dispatch via attribute scan #4

Description

@Spelkington

Parent

Part of the MagicAST restructure milestone. Phase 3 of 5. Reuses the attribute-discovery + reflection-based registry pattern established in Phase 2, applied to parser dispatch instead of JSON serialization.

What to build

Today, OracleParser.ParseClause (in libs/magic-ast/Parsing/OracleParser.cs, around line 112) contains a switch over classification.Kind that routes each clause to the appropriate parser:

return classification.Kind switch
{
  AbilityKind.Triggered  => triggeredParser.Parse(...),
  AbilityKind.Activated  => activatedParser.Parse(...),
  AbilityKind.Static     => staticParser.Parse(...),
  // ... etc
};

Adding a new AbilityKind requires editing this switch — another shared-file merge hotspot.

Replace with a parser registry:

// New interface (or similar shape — implementer's choice on exact signature):
public interface IAbilityParser
{
  Ability Parse(OracleClause clause, ClauseClassification classification);
}

// New attribute that pins a parser to an AbilityKind:
[OracleAbilityParser(AbilityKind.Triggered)]
public sealed class TriggeredAbilityParser : IAbilityParser { ... }

// New registry — scans for IAbilityParser implementations at startup,
// builds AbilityKind -> parser map, caches.
public sealed class AbilityParserRegistry { ... }

// OracleParser becomes a thin coordinator:
return _registry.GetParser(classification.Kind).Parse(clause, classification);

The FallbackParser should also be registered (likely via a special-cased attribute like [OracleAbilityParser(AbilityKind.Unparsed)] or via a [OracleAbilityFallback] attribute) so that no special-casing leaks back into OracleParser.

Acceptance criteria

  • OracleParser.ParseClause contains no switch over AbilityKind for parser dispatch.
  • ActivatedAbilityParser, StaticAbilityParser, TriggeredAbilityParser, and FallbackParser all implement the new parser interface and are decorated with the new attribute (or its fallback variant).
  • The registry scans for IAbilityParser implementations once at startup and caches the result for the process lifetime.
  • Adding a new parser is a single new file with one attribute; no edits to OracleParser.cs or any other existing file.
  • Two ability kinds claiming the same [OracleAbilityParser] attribute produces a clear startup error (don't silently let one win).
  • An AbilityKind with no registered parser produces a clear error (either at startup, if discoverable, or when first encountered).
  • Full ratchet test suite remains green or improves — no regressions in test-baseline.json.

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agentscope:magic-astAffects libs/magic-ast/type:refactorRefactor — no behavior change expected

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions