Skip to content

Commit

Permalink
feat(patterns): New M.tagged pattern maker (#2091)
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Feb 21, 2024
1 parent e9f7049 commit 4394e6e
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/patterns/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
User-visible changes in `@endo/patterns`:

# next release

- Add `M.tagged(tagPattern, payloadPattern)` for making patterns that match
Passable Tagged objects.

# v0.2.6 (2023-09-11)

- Adds support for CopyMap patterns (e.g., `matches(specimen, makeCopyMap([]))`).
31 changes: 31 additions & 0 deletions packages/patterns/src/patterns/patternMatchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,34 @@ const makePatternKit = () => {
},
});

/** @type {import('./types.js').MatchHelper} */
const matchTaggedHelper = Far('match:tagged helper', {
checkMatches: (specimen, [tagPatt, payloadPatt], check) => {
if (passStyleOf(specimen) !== 'tagged') {
return check(
false,
X`Expected tagged object, not ${q(
passStyleOf(specimen),
)}: ${specimen}`,
);
}
return (
checkMatches(getTag(specimen), tagPatt, check, 'tag') &&
checkMatches(specimen.payload, payloadPatt, check, 'payload')
);
},

checkIsWellFormed: (payload, check) =>
checkMatches(
payload,
harden([MM.pattern(), MM.pattern()]),
check,
'match:tagged payload',
),

getRankCover: (_kind, _encodePassable) => getPassStyleCover('tagged'),
});

/** @type {import('./types.js').MatchHelper} */
const matchBigintHelper = Far('match:bigint helper', {
checkMatches: (specimen, [limits = undefined], check) => {
Expand Down Expand Up @@ -1500,6 +1528,7 @@ const makePatternKit = () => {
'match:key': matchKeyHelper,
'match:pattern': matchPatternHelper,
'match:kind': matchKindHelper,
'match:tagged': matchTaggedHelper,
'match:bigint': matchBigintHelper,
'match:nat': matchNatHelper,
'match:string': matchStringHelper,
Expand Down Expand Up @@ -1604,6 +1633,8 @@ const makePatternKit = () => {
key: () => KeyShape,
pattern: () => PatternShape,
kind: makeKindMatcher,
tagged: (tagPatt = M.string(), payloadPatt = M.any()) =>
makeMatcher('match:tagged', harden([tagPatt, payloadPatt])),
boolean: () => BooleanShape,
number: () => NumberShape,
bigint: (limits = undefined) =>
Expand Down
6 changes: 6 additions & 0 deletions packages/patterns/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ export {};
* Otherwise, does not match any value.
* TODO: Reject attempts to create a kind matcher with unknown `kind`?
*
* @property {(tagPatt?: Pattern, payloadPatt?: Pattern) => Matcher} tagged
* For matching an arbitrary Passable Tagged object, whether it has a
* recognized kind or not. If `tagPatt` is omitted, it defaults to
* `M.string()`. If `payloadPatt` is omitted, it defaults to
* `M.any()`.
*
* @property {() => Matcher} boolean
* Matches `true` or `false`.
*
Expand Down
32 changes: 32 additions & 0 deletions packages/patterns/test/test-patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const runTests = (t, successCase, failCase) => {
failCase(specimen, M.and(3, 4), '3 - Must be: 4');
failCase(specimen, M.or(4, 4), '3 - Must match one of [4,4]');
failCase(specimen, M.or(), '3 - no pattern disjuncts to match: []');
failCase(specimen, M.tagged(), 'Expected tagged object, not "number": 3');
}
{
const specimen = 0n;
Expand Down Expand Up @@ -672,6 +673,37 @@ const runTests = (t, successCase, failCase) => {
'match:recordOf payload: [1]: A "promise" cannot be a pattern',
);
}
const specimen = makeTagged('Vowish', {
vowVX: Far('VowVX', {}),
});
successCase(specimen, M.any());
successCase(specimen, M.tagged());
successCase(specimen, M.tagged('Vowish'));
successCase(
specimen,
M.tagged(
'Vowish',
harden({
vowVX: M.remotable('VowVX'),
}),
),
);
failCase(
specimen,
M.record(),
'cannot check unrecognized tag "Vowish": "[Vowish]"',
);
failCase(
specimen,
M.kind('tagged'),
'cannot check unrecognized tag "Vowish": "[Vowish]"',
);
failCase(specimen, M.tagged('Vowoid'), 'tag: "Vowish" - Must be: "Vowoid"');
failCase(
specimen,
M.tagged(undefined, harden({})),
'payload: {"vowVX":"[Alleged: VowVX]"} - Must be: {}',
);
};

/**
Expand Down

0 comments on commit 4394e6e

Please sign in to comment.