190 lines
5.5 KiB
TypeScript
190 lines
5.5 KiB
TypeScript
/// SPDX-License-Identifier: GPL-3.0-or-later
|
|
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
|
|
import { canonicalString, is, KeyedDictionary, Record, RecordConstructorInfo, Value } from '@preserves/core';
|
|
import { AnyValue } from './actor.js';
|
|
import * as P from '../gen/dataspacePatterns.js';
|
|
|
|
export type Path = Array<AnyValue>;
|
|
|
|
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<Value>): 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<AnyValue> /* includes Record! */)[index as number];
|
|
}
|
|
}
|
|
|
|
export type PatternAnalysis = {
|
|
constPaths: Array<Path>,
|
|
constValues: Array<AnyValue>,
|
|
capturePaths: Array<Path>,
|
|
};
|
|
|
|
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<AnyValue> | false {
|
|
const captures: Array<AnyValue> = [];
|
|
|
|
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);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Constructor helpers
|
|
|
|
export function bind(p?: P.Pattern): P.Pattern {
|
|
return P.Pattern.DBind(P.DBind(p ?? _));
|
|
}
|
|
|
|
export function discard(): P.Pattern {
|
|
return P.Pattern.DDiscard(P.DDiscard());
|
|
}
|
|
|
|
export const _ = discard();
|
|
|
|
export function lit(v: AnyValue): P.Pattern {
|
|
return P.Pattern.DLit(P.DLit(v));
|
|
}
|
|
|
|
export function rec(label: AnyValue, ... fields: P.Pattern[]): P.Pattern {
|
|
return P.Pattern.DCompound(P.DCompound.rec({
|
|
ctor: P.CRec({
|
|
label,
|
|
arity: fields.length,
|
|
}),
|
|
members: new KeyedDictionary(fields.map((p, i) => [i, p])),
|
|
}));
|
|
}
|
|
|
|
export function arr(... patterns: P.Pattern[]): P.Pattern {
|
|
return P.Pattern.DCompound(P.DCompound.arr({
|
|
ctor: P.CArr(patterns.length),
|
|
members: new KeyedDictionary(patterns.map((p, i) => [i, p])),
|
|
}));
|
|
}
|
|
|
|
export function dict(... entries: [AnyValue, P.Pattern][]): P.Pattern {
|
|
return P.Pattern.DCompound(P.DCompound.dict({
|
|
ctor: P.CDict(),
|
|
members: new KeyedDictionary(entries),
|
|
}));
|
|
}
|