diff --git a/implementations/javascript/packages/core/src/order.ts b/implementations/javascript/packages/core/src/order.ts new file mode 100644 index 0000000..5508f11 --- /dev/null +++ b/implementations/javascript/packages/core/src/order.ts @@ -0,0 +1,105 @@ +import { is, isAnnotated } from './is'; +import { Bytes } from './bytes'; +import { Set, Dictionary } from './dictionary'; +import { Embedded, isEmbedded } from './embedded'; +import { Float } from './float'; +import { Value } from './values'; +import { Record } from './record'; + +export function typeCode(v: Value): number { + if (isAnnotated(v)) v = v.item; + switch (typeof v) { + case 'boolean': + return 0; + case 'number': + case 'bigint': + return 3; + case 'string': + return 4; + case 'symbol': + return 6; + case 'object': + if (Float.isFloat(v)) { + return Float.isSingle(v) ? 1 : 2; + } + if (Bytes.isBytes(v)) return 5; + if (Array.isArray(v)) { + return ('label' in v) ? 7 : 8; + } + if (Set.isSet(v)) return 9; + if (Dictionary.isDictionary(v)) return 10; + if (isEmbedded(v)) return 11; + /* fall through */ + default: + throw new Error("Invalid Value in typeCode"); + } +} + +export function compare( + a: Value, + b: Value, + compare_embedded: (a: V, b: V) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1, +): number { + function cmp(a: Value, b: Value): number { + if (isAnnotated(a)) a = a.item; + if (isAnnotated(b)) b = b.item; + const ta = typeCode(a); + const tb = typeCode(b); + if (ta < tb) return -1; + if (ta > tb) return 1; + switch (ta) { + case 0: + case 3: + case 4: { + const va = a as any; + const vb = b as any; + return va < vb ? -1 : va > vb ? 1 : 0; + } + case 1: + case 2: { + const va = (a as Float).value; + const vb = (b as Float).value; + return va < vb ? -1 : va > vb ? 1 : 0; + } + case 5: + return Bytes.compare(a as Bytes, b as Bytes); + case 6: { + const va = (a as symbol).description!; + const vb = (b as symbol).description!; + return va < vb ? -1 : va > vb ? 1 : 0; + } + case 7: { + const lr = cmp((a as Record, Value[], V>).label, + (b as Record, Value[], V>).label); + if (lr !== 0) return lr; + /* fall through */ + } + case 8: { + const va = a as Value[]; + const vb = b as Value[]; + const l = Math.min(va.length, vb.length) + for (let i = 0; i < l; i++) { + const c = cmp(va[i], vb[i]); + if (c !== 0) return c; + } + return va.length < vb.length ? -1 : va.length > vb.length ? 1 : 0; + } + case 9: { + const va = Array.from(a as Set).sort(cmp); + const vb = Array.from(b as Set).sort(cmp); + return cmp(va, vb); + } + case 10: { + const va = Array.from(a as Dictionary).sort(cmp); + const vb = Array.from(b as Dictionary).sort(cmp); + return cmp(va, vb); + } + case 11: + return compare_embedded((a as Embedded).embeddedValue, + (b as Embedded).embeddedValue); + default: + throw new Error("Invalid typeCode: " + ta); + } + } + return cmp(a, b); +} diff --git a/implementations/javascript/packages/core/src/runtime.ts b/implementations/javascript/packages/core/src/runtime.ts index 4433c97..f6b4a72 100644 --- a/implementations/javascript/packages/core/src/runtime.ts +++ b/implementations/javascript/packages/core/src/runtime.ts @@ -14,6 +14,7 @@ export * from './fold'; export * from './fromjs'; export * from './is'; export * from './merge'; +export * from './order'; export * from './reader'; export * from './record'; export * from './strip';