tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

io-protocol.js (3215B)


      1 export function canonicalNat(reader, handle, bits) {
      2     if (!Number.isInteger(bits) || bits < 0 || bits > 64)
      3         throw new Error('Invalid natural bit bound');
      4     let rest = handle, value = 0n, count = 0, last = 0n;
      5     for (;;) {
      6         const node = reader.observe(rest).node;
      7         if (node.kind === 'leaf')
      8             return count === 0 || last === 1n ? value : undefined;
      9         if (count === bits || node.kind !== 'fork')
     10             return undefined;
     11         const bit = reader.observe(node.left).node;
     12         if (bit.kind === 'leaf')
     13             last = 0n;
     14         else if (bit.kind === 'stem' && reader.observe(bit.child).node.kind === 'leaf')
     15             last = 1n;
     16         else
     17             return undefined;
     18         value |= last << BigInt(count++);
     19         rest = node.right;
     20     }
     21 }
     22 function name(reader, handle) {
     23     let rest = handle, value = '';
     24     for (;;) {
     25         const node = reader.observe(rest).node;
     26         if (node.kind === 'leaf')
     27             return value || undefined;
     28         // Observe only the excess spine cell, never its head or tail.
     29         if (value.length === 64 || node.kind !== 'fork')
     30             return undefined;
     31         const code = canonicalNat(reader, node.left, 7);
     32         if (code === undefined || !(code >= 97n && code <= 122n || value.length > 0 && (code >= 48n && code <= 57n || code === 45n)))
     33             return undefined;
     34         value += String.fromCharCode(Number(code));
     35         rest = node.right;
     36     }
     37 }
     38 export class ProtocolError extends Error {
     39 }
     40 export function decodeAction(reader, handle) {
     41     const action = reader.observe(handle).node;
     42     if (action.kind !== 'fork')
     43         throw new ProtocolError('invalid computation shape');
     44     const code = canonicalNat(reader, action.left, 8);
     45     if (code !== undefined && code <= 9n)
     46         return { kind: 'core', code: Number(code), payload: action.right };
     47     if (code !== 10n)
     48         throw new ProtocolError('unknown computation constructor');
     49     const group = reader.observe(action.right).node;
     50     if (group.kind !== 'fork')
     51         throw new ProtocolError('invalid facility header');
     52     const revision = reader.observe(group.right).node;
     53     if (revision.kind !== 'fork')
     54         throw new ProtocolError('invalid facility header');
     55     const operation = reader.observe(revision.right).node;
     56     if (operation.kind !== 'fork')
     57         throw new ProtocolError('invalid facility header');
     58     const g = name(reader, group.left);
     59     if (g === undefined)
     60         throw new ProtocolError('invalid facility header');
     61     const v = canonicalNat(reader, revision.left, 16);
     62     if (v === undefined || v === 0n)
     63         throw new ProtocolError('invalid facility header');
     64     const op = name(reader, operation.left);
     65     if (op === undefined)
     66         throw new ProtocolError('invalid facility header');
     67     return { kind: 'request', group: g, revision: Number(v), operation: op, arguments: operation.right };
     68 }
     69 export function header(reader, handle) {
     70     const action = decodeAction(reader, handle);
     71     return action.kind === 'core' ? { kind: action.kind, code: action.code }
     72         : { kind: action.kind, group: action.group, revision: action.revision, operation: action.operation };
     73 }