/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones import { canonicalString, is, Record, RecordConstructorInfo, Value } from '@preserves/core'; import { AnyValue } from './actor.js'; import * as P from '../gen/dataspacePatterns.js'; export type Path = Array; export type Shape = string; export function classOfValue(v: any): Shape | null { if (Record.isRecord(v)) { return constructorInfoSignature(Record.constructorInfo(v)); } else if (Array.isArray(v)) { return '' + v.length; } else if (Map.isMap(v)) { return '{}'; } else { return null; } } export function classOfCtor(v: P.DCompound): Shape { switch (v._variant) { case 'rec': return canonicalString(v.ctor.label) + '/' + v.ctor.arity; case 'arr': return '' + v.ctor.arity; case 'dict': return '{}'; } } // Called by generated code in addition to functions in this module export function constructorInfoSignature(ci: RecordConstructorInfo): string { return canonicalString(ci.label) + '/' + ci.arity; } export function step(v: AnyValue, index: AnyValue): AnyValue | undefined { if (Map.isMap(v)) { return v.get(index); } else { return (v as Array /* includes Record! */)[index as number]; } } export type PatternAnalysis = { constPaths: Array, constValues: Array, capturePaths: Array, }; export function analysePattern(p: P.Pattern): PatternAnalysis { const result: PatternAnalysis = { constPaths: [], constValues: [], capturePaths: [], }; const path: Path = []; function walk(p: P.Pattern) { switch (p._variant) { case 'DCompound': p.value.members.forEach((v, k) => { path.push(k); walk(v); path.pop(); }); break; case 'DBind': result.capturePaths.push(path.slice()); walk(p.value.pattern); break; case 'DDiscard': break; case 'DLit': result.constPaths.push(path.slice()); result.constValues.push(p.value.value); break; } } walk(p); return result; } export function match(p: P.Pattern, v: AnyValue): Array | false { const captures: Array = []; function walk(p: P.Pattern, v: AnyValue): boolean { switch (p._variant) { case 'DBind': { captures.push(v); return walk(p.value.pattern, v); } case 'DDiscard': return true; case 'DLit': return is(p.value.value, v); case 'DCompound': { const pcls = classOfCtor(p.value); const vcls = classOfValue(v); if (pcls !== vcls) return false; for (const [stepIndex, pp] of p.value.members.entries()) { const vv = step(v, stepIndex); if (vv === void 0) return false; if (!walk(pp, vv)) return false; } return true; } } } return walk(p, v) ? captures : false; } export function isCompletelyConcrete(p: P.Pattern): boolean { function walk(p: P.Pattern): boolean { switch (p._variant) { case 'DBind': return false; case 'DDiscard': return false; case 'DLit': return true; case 'DCompound': for (const pp of p.value.members.values()) { if (!walk(pp)) return false; } return true; } } return walk(p); } export function withoutCaptures(p: P.Pattern): P.Pattern { function walk(p: P.Pattern): P.Pattern { switch (p._variant) { case 'DBind': return walk(p.value.pattern); case 'DDiscard': return p; case 'DLit': return p; case 'DCompound': return P.Pattern.DCompound({ _variant: p.value._variant, ctor: p.value.ctor, members: p.value.members.mapEntries(e => [e[0], walk(e[1])]), } as P.DCompound); } } return walk(p); }