forked from syndicate-lang/preserves
WIP
This commit is contained in:
parent
2b74100b2a
commit
738a47ce90
|
@ -0,0 +1,54 @@
|
|||
"use strict";
|
||||
// Preserves Annotations.
|
||||
|
||||
if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
||||
require('../package.json').version,
|
||||
'annotations.js',
|
||||
module)) return;
|
||||
|
||||
const { Record, List, Map, Set } = 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);
|
||||
};
|
||||
|
||||
function stripAnnotations(v, depth) {
|
||||
function step(v, depth) {
|
||||
if (depth === 0) return v;
|
||||
if (!(v instanceof Annotated)) return v;
|
||||
|
||||
const nextDepth = depth - 1;
|
||||
function walk(v) { return step(v, nextDepth); }
|
||||
|
||||
if (v.item instanceof Record) {
|
||||
return Record(walk(v.item.label), 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 (v.item instanceof Annotated) {
|
||||
const e = new Error("Improper annotation structure");
|
||||
e.irritant = v;
|
||||
throw e;
|
||||
} else {
|
||||
return v.item;
|
||||
}
|
||||
}
|
||||
step(v, (depth === void 0) ? Infinity : depth);
|
||||
}
|
|
@ -7,6 +7,7 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
|||
module)) return;
|
||||
|
||||
const Values = require('./values.js');
|
||||
const Annotations = require('./annotations.js');
|
||||
const { List, Map, Set, Bytes, Record, Single, Double } = Values;
|
||||
|
||||
const { PreserveOn } = require('./symbols.js');
|
||||
|
@ -27,7 +28,7 @@ class Decoder {
|
|||
? (packet._view || packet) // strip off Bytes wrapper, if any
|
||||
: new Uint8Array(0);
|
||||
this.index = 0;
|
||||
this.shortForms = options.shortForms || {};
|
||||
this.includeAnnotations = options.includeAnnotations || false;
|
||||
}
|
||||
|
||||
write(data) {
|
||||
|
@ -75,16 +76,15 @@ class Decoder {
|
|||
return [major, minor, arg];
|
||||
}
|
||||
|
||||
peekend(arg) {
|
||||
const [a,i,r] = this.nextop();
|
||||
const result = (a === 0) && (i === 3) && (r === arg);
|
||||
peekend() {
|
||||
const result = this.nextop() === 4;
|
||||
if (!result) this.index--;
|
||||
return result;
|
||||
}
|
||||
|
||||
binarystream(arg, minor) {
|
||||
binarystream(minor) {
|
||||
const result = [];
|
||||
while (!this.peekend(arg)) {
|
||||
while (!this.peekend()) {
|
||||
const chunk = this.next();
|
||||
if (ArrayBuffer.isView(chunk)) {
|
||||
result.push(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
||||
|
@ -99,10 +99,10 @@ class Decoder {
|
|||
return this.decodebinary(minor, Bytes.concat(result));
|
||||
}
|
||||
|
||||
valuestream(arg, minor, decoder) {
|
||||
valuestream(minor) {
|
||||
const result = [];
|
||||
while (!this.peekend(arg)) result.push(this.next());
|
||||
return decoder(minor, result);
|
||||
while (!this.peekend()) result.push(this.next());
|
||||
return this.decodecompound(minor, result);
|
||||
}
|
||||
|
||||
decodeint(bs) {
|
||||
|
@ -123,23 +123,15 @@ class Decoder {
|
|||
}
|
||||
}
|
||||
|
||||
decoderecord(minor, vs) {
|
||||
if (minor === 3) {
|
||||
if (vs.length === 0) throw new DecodeError("Too few elements in encoded record");
|
||||
return new Record(vs[0], vs.slice(1));
|
||||
} else {
|
||||
const label = this.shortForms[minor];
|
||||
if (label === void 0) throw new DecodeError("Use of unconfigured short form " + minor);
|
||||
return new Record(label, vs);
|
||||
}
|
||||
}
|
||||
|
||||
decodecollection(minor, vs) {
|
||||
decodecompound(minor, vs) {
|
||||
switch (minor) {
|
||||
case 0: return List(vs);
|
||||
case 1: return Set(vs);
|
||||
case 2: return this.mapFromArray(vs);
|
||||
case 3: throw new DecodeError("Invalid collection type");
|
||||
case 0: {
|
||||
if (vs.length === 0) throw new DecodeError("Too few elements in encoded record");
|
||||
return new Record(vs[0], vs.slice(1));
|
||||
}
|
||||
case 1: return List(vs);
|
||||
case 2: return Set(vs);
|
||||
case 3: return this.mapFromArray(vs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,28 +154,31 @@ class Decoder {
|
|||
case 1: return true;
|
||||
case 2: return Single(this.nextbytes(4).getFloat32(0, false));
|
||||
case 3: return Double(this.nextbytes(8).getFloat64(0, false));
|
||||
case 4: throw new DecodeError("Unexpected end-of-stream marker");
|
||||
case 5: ...annotation...;
|
||||
default: throw new DecodeError("Illegal format A lead byte");
|
||||
}
|
||||
case 1:
|
||||
return (arg > 12) ? arg - 16 : arg;
|
||||
...placeholder...;
|
||||
case 2: {
|
||||
const t = arg >> 2;
|
||||
const n = arg & 3;
|
||||
switch (t) {
|
||||
case 0: throw new DecodeError("Invalid format C start byte");
|
||||
case 1: return this.binarystream(arg, n);
|
||||
case 2: return this.valuestream(arg, n, this.decoderecord.bind(this));
|
||||
case 3: return this.valuestream(arg, n, this.decodecollection.bind(this));
|
||||
case 0: throw new DecodeError("Invalid format C start byte (0)");
|
||||
case 1: return this.binarystream(n);
|
||||
case 2: return this.valuestream(n);
|
||||
case 3: throw new DecodeError("Invalid format C start byte (3)");
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
throw new DecodeError("Invalid format C end byte");
|
||||
return (arg > 12) ? arg - 16 : arg;
|
||||
}
|
||||
case 1:
|
||||
return this.decodebinary(minor, Bytes.from(this.nextbytes(this.wirelength(arg))));
|
||||
case 2:
|
||||
return this.decoderecord(minor, this.nextvalues(this.wirelength(arg)));
|
||||
return this.decodecompound(minor, this.nextvalues(this.wirelength(arg)));
|
||||
case 3:
|
||||
return this.decodecollection(minor, this.nextvalues(this.wirelength(arg)));
|
||||
throw new DecodeError("Invalid lead byte (major 3)");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +202,6 @@ class Encoder {
|
|||
this.chunks = [];
|
||||
this.view = new DataView(new ArrayBuffer(256));
|
||||
this.index = 0;
|
||||
this.shortForms = options.shortForms || {};
|
||||
}
|
||||
|
||||
contents() {
|
||||
|
@ -275,7 +269,7 @@ class Encoder {
|
|||
}
|
||||
|
||||
encodecollection(minor, items) {
|
||||
this.header(3, minor, items.size);
|
||||
this.header(2, minor, items.size);
|
||||
for (const item of items) { this.push(item); }
|
||||
}
|
||||
|
||||
|
@ -283,10 +277,11 @@ class Encoder {
|
|||
const tn = ((t & 3) << 2) | (n & 3);
|
||||
this.header(0, 2, tn);
|
||||
for (const item of items) { this.push(item); }
|
||||
this.header(0, 3, tn);
|
||||
this.header(0, 0, 4);
|
||||
}
|
||||
|
||||
push(v) {
|
||||
...placeholder...;
|
||||
if (typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function') {
|
||||
v[PreserveOn](this);
|
||||
}
|
||||
|
@ -295,7 +290,7 @@ class Encoder {
|
|||
}
|
||||
else if (typeof v === 'number') {
|
||||
if (v >= -3 && v <= 12) {
|
||||
this.leadbyte(0, 1, v >= 0 ? v : v + 16);
|
||||
this.leadbyte(0, 3, v >= 0 ? v : v + 16);
|
||||
} else {
|
||||
this.encodeint(v);
|
||||
}
|
||||
|
@ -323,18 +318,18 @@ class Encoder {
|
|||
}
|
||||
}
|
||||
else if (List.isList(v)) {
|
||||
this.encodecollection(0, v);
|
||||
}
|
||||
else if (Set.isSet(v)) {
|
||||
this.encodecollection(1, v);
|
||||
}
|
||||
else if (Set.isSet(v)) {
|
||||
this.encodecollection(2, v);
|
||||
}
|
||||
else if (Map.isMap(v)) {
|
||||
this.encodecollection(2, List().withMutations((l) => {
|
||||
this.encodecollection(3, List().withMutations((l) => {
|
||||
v.forEach((val, key) => { l.push(key).push(val); });
|
||||
}));
|
||||
}
|
||||
else if (typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function') {
|
||||
this.encodestream(3, 0, v);
|
||||
this.encodestream(2, 0, v);
|
||||
}
|
||||
else {
|
||||
throw new EncodeError("Cannot encode", v);
|
||||
|
|
|
@ -8,3 +8,4 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
|||
Object.assign(module.exports, require('./symbols.js'));
|
||||
Object.assign(module.exports, require('./codec.js'));
|
||||
Object.assign(module.exports, require('./values.js'));
|
||||
Object.assign(module.exports, require('./annotations.js'));
|
||||
|
|
|
@ -10,7 +10,7 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
|||
const util = require('util');
|
||||
|
||||
const Immutable = require('immutable');
|
||||
const { List, isList, Map, Set, is } = Immutable;
|
||||
const { List, Map, Set, is } = Immutable;
|
||||
|
||||
const { PreserveOn, AsPreserve } = require('./symbols.js');
|
||||
|
||||
|
@ -341,16 +341,8 @@ Record.prototype.set = function (index, newValue) {
|
|||
};
|
||||
|
||||
Record.prototype[PreserveOn] = function (encoder) {
|
||||
if (is(encoder.shortForms[0], this.label)) {
|
||||
encoder.header(2, 0, this.fields.size);
|
||||
} else if (is(encoder.shortForms[1], this.label)) {
|
||||
encoder.header(2, 1, this.fields.size);
|
||||
} else if (is(encoder.shortForms[2], this.label)) {
|
||||
encoder.header(2, 2, this.fields.size);
|
||||
} else {
|
||||
encoder.header(2, 3, this.fields.size + 1);
|
||||
encoder.push(this.label);
|
||||
}
|
||||
encoder.header(2, 0, this.fields.size + 1);
|
||||
encoder.push(this.label);
|
||||
for (const field of this.fields) { encoder.push(field); }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue