1+ /* eslint-disable @endo/no-optional-chaining */
12// @ts -nocheck So many errors that the suppressions hamper readability.
23// TODO parameterize MatchHelper which will solve most of them
4+ import { q , b , X , Fail , makeError , annotateError } from '@endo/errors' ;
5+ import { identChecker } from '@endo/common/ident-checker.js' ;
6+ import { applyLabelingError } from '@endo/common/apply-labeling-error.js' ;
7+ import { fromUniqueEntries } from '@endo/common/from-unique-entries.js' ;
8+ import { listDifference } from '@endo/common/list-difference.js' ;
39import {
410 assertChecker ,
511 Far ,
@@ -8,19 +14,16 @@ import {
814 passStyleOf ,
915 hasOwnPropertyOf ,
1016 nameForPassableSymbol ,
17+ } from '@endo/pass-style' ;
18+ import {
1119 compareRank ,
1220 getPassStyleCover ,
1321 intersectRankCovers ,
1422 unionRankCovers ,
1523 recordNames ,
1624 recordValues ,
1725} from '@endo/marshal' ;
18- import { identChecker } from '@endo/common/ident-checker.js' ;
19- import { applyLabelingError } from '@endo/common/apply-labeling-error.js' ;
20- import { fromUniqueEntries } from '@endo/common/from-unique-entries.js' ;
21- import { listDifference } from '@endo/common/list-difference.js' ;
2226
23- import { q , b , X , Fail , makeError , annotateError } from '@endo/errors' ;
2427import { keyEQ , keyGT , keyGTE , keyLT , keyLTE } from '../keys/compareKeys.js' ;
2528import {
2629 assertKey ,
@@ -33,12 +36,14 @@ import {
3336 checkCopyBag ,
3437 getCopyMapEntryArray ,
3538 makeCopyMap ,
39+ makeCopySet ,
40+ makeCopyBag ,
3641} from '../keys/checkKey.js' ;
3742import { generateCollectionPairEntries } from '../keys/keycollection-operators.js' ;
3843
3944/**
40- * @import {Checker, CopyRecord, CopyTagged, Passable} from '@endo/pass-style'
41- * @import {ArgGuard, AwaitArgGuard, CheckPattern, GetRankCover, InterfaceGuard, MatcherNamespace, MethodGuard, MethodGuardMaker, Pattern, RawGuard, SyncValueGuard, Kind, Limits, AllLimits, Key, DefaultGuardType} from '../types.js'
45+ * @import {Checker, CopyArray, CopyRecord, CopyTagged, Passable} from '@endo/pass-style'
46+ * @import {CopySet, CopyBag, ArgGuard, AwaitArgGuard, CheckPattern, GetRankCover, InterfaceGuard, MatcherNamespace, MethodGuard, MethodGuardMaker, Pattern, RawGuard, SyncValueGuard, Kind, Limits, AllLimits, Key, DefaultGuardType} from '../types.js'
4247 * @import {MatchHelper, PatternKit} from './types.js'
4348 */
4449
@@ -1258,6 +1263,230 @@ const makePatternKit = () => {
12581263 getRankCover : ( ) => getPassStyleCover ( 'tagged' ) ,
12591264 } ) ;
12601265
1266+ /**
1267+ * @param {CopyArray } elements
1268+ * @param {Pattern } elementPatt
1269+ * @param {bigint } bound Must be >= 1n
1270+ * @param {CopyArray } [inResults]
1271+ * @param {CopyArray } [outResults]
1272+ * @param {Checker } [check]
1273+ * @returns {boolean }
1274+ */
1275+ const elementsHasSplit = (
1276+ elements ,
1277+ elementPatt ,
1278+ bound ,
1279+ inResults = undefined ,
1280+ outResults = undefined ,
1281+ check = identChecker ,
1282+ ) => {
1283+ let count = 0n ;
1284+ // Since this feature is motivated by ERTP's use on
1285+ // non-fungible (`set`, `copySet`) amounts,
1286+ // their arrays store their elements in decending lexicographic order.
1287+ // But this function has to make some choice amoung equally good minimal
1288+ // results. It is more intuitive for the choice to be the first `bound`
1289+ // matching elements in ascending lexicigraphic order, rather than
1290+ // decending. Thus we iterate `elements` in reverse order.
1291+ for ( let i = elements . length - 1 ; i >= 0 ; i -= 1 ) {
1292+ const element = elements [ i ] ;
1293+ if ( count < bound ) {
1294+ if ( matches ( element , elementPatt ) ) {
1295+ count += 1n ;
1296+ inResults ?. push ( element ) ;
1297+ } else {
1298+ outResults ?. push ( element ) ;
1299+ }
1300+ } else if ( outResults === undefined ) {
1301+ break ;
1302+ } else {
1303+ outResults . push ( element ) ;
1304+ }
1305+ }
1306+ return check (
1307+ count >= bound ,
1308+ X `Has only ${ q ( count ) } matches, but needs ${ q ( bound ) } ` ,
1309+ ) ;
1310+ } ;
1311+
1312+ /**
1313+ * @param {CopyArray<[Key, bigint]> } pairs
1314+ * @param {Pattern } elementPatt
1315+ * @param {bigint } bound Must be >= 1n
1316+ * @param {CopyArray<[Key, bigint]> } [inResults]
1317+ * @param {CopyArray<[Key, bigint]> } [outResults]
1318+ * @param {Checker } [check]
1319+ * @returns {boolean }
1320+ */
1321+ const pairsHasSplit = (
1322+ pairs ,
1323+ elementPatt ,
1324+ bound ,
1325+ inResults = undefined ,
1326+ outResults = undefined ,
1327+ check = identChecker ,
1328+ ) => {
1329+ let count = 0n ;
1330+ // Since this feature is motivated by ERTP's use on
1331+ // semi-fungible (`copyBag`) amounts,
1332+ // their arrays store their elements in decending lexicographic order.
1333+ // But this function has to make some choice amoung equally good minimal
1334+ // results. It is more intuitive for the choice to be the first `bound`
1335+ // matching elements in ascending lexicigraphic order, rather than
1336+ // decending. Thus we iterate `pairs` in reverse order.
1337+ for ( let i = pairs . length - 1 ; i >= 0 ; i -= 1 ) {
1338+ const [ element , num ] = pairs [ i ] ;
1339+ const numRest = bound - count ;
1340+ if ( numRest >= 1n ) {
1341+ if ( matches ( element , elementPatt ) ) {
1342+ if ( num <= numRest ) {
1343+ count += num ;
1344+ inResults ?. push ( [ element , num ] ) ;
1345+ } else {
1346+ const numIn = numRest ;
1347+ count += numIn ;
1348+ inResults ?. push ( [ element , numRest ] ) ;
1349+ outResults ?. push ( [ element , num - numRest ] ) ;
1350+ }
1351+ } else {
1352+ outResults ?. push ( [ element , num ] ) ;
1353+ }
1354+ } else if ( outResults === undefined ) {
1355+ break ;
1356+ } else {
1357+ outResults . push ( [ element , num ] ) ;
1358+ }
1359+ }
1360+ return check (
1361+ count >= bound ,
1362+ X `Has only ${ q ( count ) } matches, but needs ${ q ( bound ) } ` ,
1363+ ) ;
1364+ } ;
1365+
1366+ /**
1367+ * @typedef {CopyArray | CopySet | CopyBag } Container
1368+ * @param {Container } specimen
1369+ * @param {Pattern } elementPatt
1370+ * @param {bigint } bound Must be >= 1n
1371+ * @param {boolean } [needInResults]
1372+ * @param {boolean } [needOutResults]
1373+ * @param {Checker } [check]
1374+ * @returns {[Container | undefined, Container | undefined] | false }
1375+ */
1376+ const containerHasSplit = (
1377+ specimen ,
1378+ elementPatt ,
1379+ bound ,
1380+ needInResults = false ,
1381+ needOutResults = false ,
1382+ check = identChecker ,
1383+ ) => {
1384+ const inResults = needInResults ? [ ] : undefined ;
1385+ const outResults = needOutResults ? [ ] : undefined ;
1386+ const kind = kindOf ( specimen ) ;
1387+ switch ( kind ) {
1388+ case 'copyArray' : {
1389+ if (
1390+ ! elementsHasSplit (
1391+ specimen ,
1392+ elementPatt ,
1393+ bound ,
1394+ inResults ,
1395+ outResults ,
1396+ check ,
1397+ )
1398+ ) {
1399+ // check logic already performed by elementsHasSplit
1400+ return false ;
1401+ }
1402+ return [ inResults , outResults ] ;
1403+ }
1404+ case 'copySet' : {
1405+ if (
1406+ ! elementsHasSplit (
1407+ specimen . payload ,
1408+ elementPatt ,
1409+ bound ,
1410+ inResults ,
1411+ outResults ,
1412+ check ,
1413+ )
1414+ ) {
1415+ return false ;
1416+ }
1417+ return [
1418+ inResults && makeCopySet ( inResults ) ,
1419+ inResults && makeCopySet ( inResults ) ,
1420+ ] ;
1421+ }
1422+ case 'copyBag' : {
1423+ if (
1424+ ! pairsHasSplit (
1425+ specimen . payload ,
1426+ elementPatt ,
1427+ bound ,
1428+ inResults ,
1429+ outResults ,
1430+ check ,
1431+ )
1432+ ) {
1433+ return false ;
1434+ }
1435+ return [
1436+ inResults && makeCopyBag ( inResults ) ,
1437+ inResults && makeCopyBag ( inResults ) ,
1438+ ] ;
1439+ }
1440+ default : {
1441+ return check ( false , X `unexpected ${ q ( kind ) } ` ) ;
1442+ }
1443+ }
1444+ } ;
1445+
1446+ /** @type {MatchHelper } */
1447+ const matchContainerHasHelper = Far ( 'M.containerHas helper' , {
1448+ /**
1449+ * @param {CopyArray | CopySet | CopyBag } specimen
1450+ * @param {[Pattern, bigint, Limits?] } payload
1451+ * @param {Checker } check
1452+ */
1453+ checkMatches : (
1454+ specimen ,
1455+ [ elementPatt , bound , limits = undefined ] ,
1456+ check ,
1457+ ) => {
1458+ const kind = kindOf ( specimen , check ) ;
1459+ const { decimalDigitsLimit } = limit ( limits ) ;
1460+ if (
1461+ ! applyLabelingError (
1462+ checkDecimalDigitsLimit ,
1463+ [ bound , decimalDigitsLimit , check ] ,
1464+ `${ kind } matches` ,
1465+ )
1466+ ) {
1467+ return false ;
1468+ }
1469+ return ! ! containerHasSplit (
1470+ specimen ,
1471+ elementPatt ,
1472+ bound ,
1473+ false ,
1474+ false ,
1475+ check ,
1476+ ) ;
1477+ } ,
1478+
1479+ checkIsWellFormed : ( payload , check ) =>
1480+ checkIsWellFormedWithLimit (
1481+ payload ,
1482+ harden ( [ MM . pattern ( ) , MM . gte ( 1n ) ] ) ,
1483+ check ,
1484+ 'M.containerHas payload' ,
1485+ ) ,
1486+
1487+ getRankCover : ( ) => getPassStyleCover ( 'tagged' ) ,
1488+ } ) ;
1489+
12611490 /** @type {MatchHelper } */
12621491 const matchMapOfHelper = Far ( 'match:mapOf helper' , {
12631492 checkMatches : (
@@ -1548,6 +1777,7 @@ const makePatternKit = () => {
15481777 'match:recordOf' : matchRecordOfHelper ,
15491778 'match:setOf' : matchSetOfHelper ,
15501779 'match:bagOf' : matchBagOfHelper ,
1780+ 'M.containerHas' : matchContainerHasHelper ,
15511781 'match:mapOf' : matchMapOfHelper ,
15521782 'match:splitArray' : matchSplitArrayHelper ,
15531783 'match:splitRecord' : matchSplitRecordHelper ,
@@ -1702,6 +1932,8 @@ const makePatternKit = () => {
17021932 makeLimitsMatcher ( 'match:setOf' , [ keyPatt , limits ] ) ,
17031933 bagOf : ( keyPatt = M . any ( ) , countPatt = M . any ( ) , limits = undefined ) =>
17041934 makeLimitsMatcher ( 'match:bagOf' , [ keyPatt , countPatt , limits ] ) ,
1935+ containerHas : ( elementPatt = M . any ( ) , countPatt = 1n , limits = undefined ) =>
1936+ makeLimitsMatcher ( 'M.containerHas' , [ elementPatt , countPatt , limits ] ) ,
17051937 mapOf : ( keyPatt = M . any ( ) , valuePatt = M . any ( ) , limits = undefined ) =>
17061938 makeLimitsMatcher ( 'match:mapOf' , [ keyPatt , valuePatt , limits ] ) ,
17071939 splitArray : ( base , optional = undefined , rest = undefined ) =>
@@ -1763,6 +1995,7 @@ const makePatternKit = () => {
17631995 getRankCover,
17641996 M,
17651997 kindOf,
1998+ containerHasSplit,
17661999 } ) ;
17672000} ;
17682001
@@ -1781,6 +2014,7 @@ export const {
17812014 getRankCover,
17822015 M,
17832016 kindOf,
2017+ containerHasSplit,
17842018} = makePatternKit ( ) ;
17852019
17862020MM = M ;
0 commit comments