preserves/implementations/javascript/src/annotations.js

90 lines
2.2 KiB
JavaScript

"use strict";
// Preserves Annotations.
if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
require('../package.json').version,
'annotations.js',
module)) return;
const { Record, List, Map, Set, is, hash } = require('./values.js');
const { PreserveOn, AsPreserve } = require('./symbols.js');
function Annotated(item) {
this.annotations = [];
this.item = item;
}
Annotated.prototype[AsPreserve] = function () {
return this;
};
Annotated.prototype[PreserveOn] = function (encoder) {
for (const a of this.annotations) {
encoder.header(0, 0, 5);
encoder.push(a);
}
encoder.push(this.item);
};
Annotated.prototype.strip = function (depth) {
return stripAnnotations(this, depth);
};
Annotated.prototype.peel = function () {
return stripAnnotations(this, 1);
};
Annotated.prototype.equals = function (other) {
return isAnnotated(other) && is(this.item, other.item);
};
Annotated.prototype.hashCode = function () {
return hash(this.item);
};
function isAnnotated(v) {
return (v instanceof Annotated);
}
function stripAnnotations(v, depth) {
function step(v, depth) {
if (depth === 0) return v;
if (!isAnnotated(v)) return v;
const nextDepth = depth - 1;
function walk(v) { return step(v, nextDepth); }
if (v.item instanceof Record) {
return new Record(step(v.item.label, depth), v.item.fields.map(walk));
} else if (List.isList(v.item)) {
return v.item.map(walk);
} else if (Set.isSet(v.item)) {
return v.item.map(walk);
} else if (Map.isMap(v.item)) {
return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]);
} else if (isAnnotated(v.item)) {
const e = new Error("Improper annotation structure");
e.irritant = v;
throw e;
} else {
return v.item;
}
}
return step(v, (depth === void 0) ? Infinity : depth);
}
function annotate(v, ...anns) {
if (!isAnnotated(v)) {
v = new Annotated(v);
}
anns.forEach((a) => v.annotations.push(a));
return v;
}
Object.assign(module.exports, {
Annotated,
isAnnotated,
stripAnnotations,
annotate,
});