@mark1russell7/coeval 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -759,7 +759,7 @@ function fits(found, expected, domains) {
759
759
  return fitsAt(found, expected, domains, 0);
760
760
  }
761
761
  /** This function sets an own field of a new record. Only the key `__proto__` needs `defineProperty`. */
762
- function setOwn$4(target, key, value) {
762
+ function setOwn$5(target, key, value) {
763
763
  if (key === "__proto__") Object.defineProperty(target, key, {
764
764
  value,
765
765
  enumerable: true,
@@ -778,10 +778,10 @@ function joinRecords(a, b, domains, depth) {
778
778
  const inA = Object.hasOwn(a.fields, key);
779
779
  const inB = Object.hasOwn(b.fields, key);
780
780
  if (inA && inB) {
781
- setOwn$4(fields, key, joinAt(a.fields[key], b.fields[key], domains, depth));
781
+ setOwn$5(fields, key, joinAt(a.fields[key], b.fields[key], domains, depth));
782
782
  if (a.optional.has(key) || b.optional.has(key)) optional.push(key);
783
783
  } else if (closed) {
784
- setOwn$4(fields, key, inA ? a.fields[key] : b.fields[key]);
784
+ setOwn$5(fields, key, inA ? a.fields[key] : b.fields[key]);
785
785
  optional.push(key);
786
786
  }
787
787
  }
@@ -1185,7 +1185,7 @@ function keyOf(path) {
1185
1185
  return JSON.stringify(path.map(String));
1186
1186
  }
1187
1187
  /** This function sets an own data property. An assignment to the key `__proto__` changes the prototype. */
1188
- function setOwn$3(target, key, value) {
1188
+ function setOwn$4(target, key, value) {
1189
1189
  if (key === "__proto__") Object.defineProperty(target, key, {
1190
1190
  value,
1191
1191
  enumerable: true,
@@ -1202,7 +1202,7 @@ function defined(value) {
1202
1202
  if (Array.isArray(value)) return value.map(defined);
1203
1203
  if (!isRecord(value)) return value;
1204
1204
  const out = {};
1205
- for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$3(out, key, defined(value[key]));
1205
+ for (const key of Object.keys(value)) if (value[key] !== void 0) setOwn$4(out, key, defined(value[key]));
1206
1206
  return out;
1207
1207
  }
1208
1208
  /** Two values are the same when they are equal by `docEquals`. A function and a host value are equal only to themselves. */
@@ -1247,7 +1247,7 @@ function extendDomain(name, more) {
1247
1247
  ...Object.keys(steps).length === 0 ? {} : { steps }
1248
1248
  };
1249
1249
  const domains = {};
1250
- setOwn$3(domains, name, domain);
1250
+ setOwn$4(domains, name, domain);
1251
1251
  return {
1252
1252
  domains,
1253
1253
  replaces: [["domains", name]]
@@ -1297,10 +1297,10 @@ function mergeLeaf(m, table, key, later, path, part, replaces, equal, reason) {
1297
1297
  if (later === void 0) return;
1298
1298
  const earlier = own$2(table, key);
1299
1299
  if (earlier === void 0) {
1300
- setOwn$3(table, key, later);
1300
+ setOwn$4(table, key, later);
1301
1301
  m.give(path, part);
1302
1302
  } else if (replaces.has(keyOf(path))) {
1303
- setOwn$3(table, key, later);
1303
+ setOwn$4(table, key, later);
1304
1304
  m.replace(path, part);
1305
1305
  } else if (!equal(earlier, later)) m.clash(path, part, reason);
1306
1306
  }
@@ -1321,9 +1321,9 @@ function mergeEntries(m, into, from, path, part, replaces) {
1321
1321
  const at = [...path, key];
1322
1322
  const earlier = own$2(into, key);
1323
1323
  if (earlier === void 0) {
1324
- setOwn$3(into, key, later);
1324
+ setOwn$4(into, key, later);
1325
1325
  m.give(at, part);
1326
- } else setOwn$3(into, key, mergeEntry(m, earlier, later, at, part, replaces));
1326
+ } else setOwn$4(into, key, mergeEntry(m, earlier, later, at, part, replaces));
1327
1327
  }
1328
1328
  }
1329
1329
  /** This function merges two entries at one path. Two namespaces merge key by key, and their fallbacks merge too. */
@@ -1476,8 +1476,8 @@ function mergeCode(m, draft, part, index, replaces) {
1476
1476
  const runnerPath = ["runners", name];
1477
1477
  if (replaces.has(keyOf(evaluatorPath)) || replaces.has(keyOf(runnerPath))) {
1478
1478
  if (evaluator === void 0) delete draft.evaluators[name];
1479
- else setOwn$3(draft.evaluators, name, evaluator);
1480
- if (runner !== void 0) setOwn$3(draft.runners ??= {}, name, runner);
1479
+ else setOwn$4(draft.evaluators, name, evaluator);
1480
+ if (runner !== void 0) setOwn$4(draft.runners ??= {}, name, runner);
1481
1481
  else if (draft.runners !== void 0) delete draft.runners[name];
1482
1482
  m.replace(evaluatorPath, index);
1483
1483
  m.replace(runnerPath, index);
@@ -1600,7 +1600,7 @@ function removeEntry(entries, path) {
1600
1600
  }
1601
1601
  const out = { ...entries };
1602
1602
  if (next === void 0) delete out[key];
1603
- else setOwn$3(out, key, next);
1603
+ else setOwn$4(out, key, next);
1604
1604
  return out;
1605
1605
  }
1606
1606
  /**
@@ -2412,7 +2412,7 @@ var Bad = class {
2412
2412
  }
2413
2413
  };
2414
2414
  /** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
2415
- function setOwn$2(target, key, value) {
2415
+ function setOwn$3(target, key, value) {
2416
2416
  if (key === "__proto__") Object.defineProperty(target, key, {
2417
2417
  value,
2418
2418
  enumerable: true,
@@ -2584,7 +2584,7 @@ function splitParts(shape, value) {
2584
2584
  const out = {};
2585
2585
  for (const [key, child] of node.fields) {
2586
2586
  const kept = strip(child);
2587
- if (kept !== void 0) setOwn$2(out, key, kept);
2587
+ if (kept !== void 0) setOwn$3(out, key, kept);
2588
2588
  }
2589
2589
  return out;
2590
2590
  };
@@ -2593,7 +2593,7 @@ function splitParts(shape, value) {
2593
2593
  if (node.t === "slot") return handles[node.index];
2594
2594
  if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
2595
2595
  const out = {};
2596
- for (const [key, child] of node.fields) setOwn$2(out, key, fillWith(child, handles));
2596
+ for (const [key, child] of node.fields) setOwn$3(out, key, fillWith(child, handles));
2597
2597
  return out;
2598
2598
  };
2599
2599
  return {
@@ -3555,7 +3555,7 @@ function fillOf(split, doc) {
3555
3555
  return (items) => split.parts.length === 0 ? doc.value : split.fill(items);
3556
3556
  }
3557
3557
  function interpret(fn, input, fill) {
3558
- const { kind, typePath, doc, data, place } = input;
3558
+ const { kind, typePath, doc, data, place, machine } = input;
3559
3559
  const address = input.address === true ? { address: true } : {};
3560
3560
  const refill = input.fill;
3561
3561
  const compiled = input.parts;
@@ -3576,6 +3576,7 @@ function interpret(fn, input, fill) {
3576
3576
  parts,
3577
3577
  context: ctx,
3578
3578
  place,
3579
+ machine,
3579
3580
  ...address,
3580
3581
  resolve: (path, options) => rt.resolve(ctx, path, options),
3581
3582
  effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
@@ -3584,7 +3585,7 @@ function interpret(fn, input, fill) {
3584
3585
  };
3585
3586
  }
3586
3587
  function interpretRun(fn, input, fill) {
3587
- const { kind, typePath, doc, data, place } = input;
3588
+ const { kind, typePath, doc, data, place, machine } = input;
3588
3589
  const refill = input.fill;
3589
3590
  const compiled = input.parts;
3590
3591
  const hasParts = compiled.length > 0;
@@ -3604,6 +3605,7 @@ function interpretRun(fn, input, fill) {
3604
3605
  parts,
3605
3606
  context: ctx,
3606
3607
  place,
3608
+ machine,
3607
3609
  resolve: (path, options) => rt.resolve(ctx, path, options),
3608
3610
  effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
3609
3611
  all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options),
@@ -5873,6 +5875,178 @@ function checkLatent(machine, declared, body, options = {}) {
5873
5875
  return issues;
5874
5876
  }
5875
5877
  //#endregion
5878
+ //#region src/codec.ts
5879
+ /** This function gives the codec of a domain, or `undefined` when the domain has no codec. */
5880
+ function codecOf(domain) {
5881
+ const codec = domain.codec;
5882
+ if (typeof codec !== "object" || codec === null) return void 0;
5883
+ const { encode, decode } = codec;
5884
+ return typeof encode === "function" && typeof decode === "function" ? codec : void 0;
5885
+ }
5886
+ /**
5887
+ * This function reads the value of an extension `ext.domain` (EXT.DOMAIN). The value is a record with only the text
5888
+ * `domain` and the JSON `data`. The codec of the named domain reads the data, and the result must be a member of the
5889
+ * domain.
5890
+ *
5891
+ * @param data - The value of the extension document.
5892
+ * @param domains - The domains of the machine.
5893
+ * @returns The member, `#NAME?` for a domain without a codec, or `#VALUE!` for data that the codec cannot read.
5894
+ */
5895
+ function decodeDomain(data, domains) {
5896
+ if (!isRecord(data) || typeof data["domain"] !== "string" || !Object.hasOwn(data, "data") || Object.keys(data).length !== 2) return fail("#VALUE!", "The value of ext.domain is a record with only the text domain and the data.");
5897
+ const name = data["domain"];
5898
+ const domain = domainOf(name, domains);
5899
+ if (domain === void 0) return fail("#NAME?", `The machine has no domain ${name}.`);
5900
+ const codec = codecOf(domain);
5901
+ if (codec === void 0) return fail("#NAME?", `The domain ${name} has no codec.`);
5902
+ let value;
5903
+ try {
5904
+ value = codec.decode(data["data"]);
5905
+ } catch (error) {
5906
+ return fail("#VALUE!", `The codec of the domain ${name} cannot read the data: ${describe(error)}`, { cause: error });
5907
+ }
5908
+ if (!member(value, name, domains)) return fail("#VALUE!", `The codec of the domain ${name} gives a value that is not a member of the domain.`);
5909
+ return ok(value);
5910
+ }
5911
+ /**
5912
+ * This function gives the static type of an extension `ext.domain`: the name of its domain when that domain has a
5913
+ * codec. A run then gives a member of the domain or an error, because `decodeDomain` tests the membership.
5914
+ *
5915
+ * @param data - The value of the extension document.
5916
+ * @param domains - The domains of the machine.
5917
+ * @returns The name of the domain, or `unknown`.
5918
+ */
5919
+ function domainTypeOf(data, domains) {
5920
+ if (!isRecord(data) || typeof data["domain"] !== "string") return "unknown";
5921
+ const domain = domainOf(data["domain"], domains);
5922
+ return domain !== void 0 && codecOf(domain) !== void 0 ? data["domain"] : "unknown";
5923
+ }
5924
+ /** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
5925
+ function setOwn$2(target, key, value) {
5926
+ if (key === "__proto__") Object.defineProperty(target, key, {
5927
+ value,
5928
+ enumerable: true,
5929
+ writable: true,
5930
+ configurable: true
5931
+ });
5932
+ else target[key] = value;
5933
+ }
5934
+ /**
5935
+ * This function tells if JSON writes a value exactly. Such a value is a finite number other than `-0`, a text, a truth
5936
+ * value, `null`, or a container of such values.
5937
+ */
5938
+ function isJson(value, encoder) {
5939
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
5940
+ if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0);
5941
+ const list = Array.isArray(value);
5942
+ if (!list && !isRecord(value)) return false;
5943
+ const container = value;
5944
+ const known = encoder.json.get(container);
5945
+ if (known !== void 0) return known;
5946
+ encoder.json.set(container, false);
5947
+ let all = true;
5948
+ if (list) {
5949
+ const items = value;
5950
+ for (let index = 0; index < items.length && all; index += 1) all = isJson(items[index], encoder);
5951
+ } else {
5952
+ const record = value;
5953
+ for (const key of Object.keys(record)) if (!isJson(record[key], encoder)) {
5954
+ all = false;
5955
+ break;
5956
+ }
5957
+ }
5958
+ encoder.json.set(container, all);
5959
+ return all;
5960
+ }
5961
+ /** This function gives the text of a number that JSON cannot write, for `ext.number`. */
5962
+ function numberText(value) {
5963
+ if (Number.isNaN(value)) return "NaN";
5964
+ if (value === Number.POSITIVE_INFINITY) return "Infinity";
5965
+ if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
5966
+ return "-0";
5967
+ }
5968
+ /** This function gives the term document of a value in one call of `encodeValue`. */
5969
+ function encodeTerm(value, encoder) {
5970
+ if (isJson(value, encoder)) return isEnvelope(value) ? {
5971
+ type: "lit",
5972
+ value
5973
+ } : value;
5974
+ if (value === void 0) return {
5975
+ type: ["ext", "undefined"],
5976
+ value: null
5977
+ };
5978
+ if (typeof value === "number") return {
5979
+ type: ["ext", "number"],
5980
+ value: numberText(value)
5981
+ };
5982
+ for (const [name, codec] of encoder.codecs) if (member(value, name, encoder.domains)) return {
5983
+ type: ["ext", "domain"],
5984
+ value: {
5985
+ domain: name,
5986
+ data: codec.encode(value)
5987
+ }
5988
+ };
5989
+ if (!Array.isArray(value) && !isRecord(value)) throw new TypeError(`encodeValue cannot write the value ${describe(value)}: no domain of the machine with a codec holds it.`);
5990
+ if (encoder.open.has(value)) throw new TypeError("encodeValue cannot write a value with a cycle.");
5991
+ encoder.open.add(value);
5992
+ let out;
5993
+ if (Array.isArray(value)) out = {
5994
+ type: "list",
5995
+ value: Array.from(value, (item) => encodeTerm(item, encoder))
5996
+ };
5997
+ else {
5998
+ const fields = {};
5999
+ const record = value;
6000
+ for (const key of Object.keys(record)) setOwn$2(fields, key, encodeTerm(record[key], encoder));
6001
+ out = {
6002
+ type: "record",
6003
+ value: fields
6004
+ };
6005
+ }
6006
+ encoder.open.delete(value);
6007
+ return out;
6008
+ }
6009
+ /**
6010
+ * This function writes a host value as a term document of the formula kit (EXT.ENCODE, D40). Thus the same value has
6011
+ * the same document in each language (D12).
6012
+ *
6013
+ * - A value that JSON writes exactly stays as it is. A literal with the shape of an envelope goes into `lit`.
6014
+ * - `NaN`, an infinity and `-0` become `ext.number`, and `undefined` becomes `ext.undefined`.
6015
+ * - A member of a domain with a codec becomes `ext.domain`. The domains go in the order of their names.
6016
+ * - A list or a plain record with such a value becomes the construct `list` or `record`.
6017
+ *
6018
+ * @param machine - The machine, or only its domains.
6019
+ * @param value - The host value.
6020
+ * @returns The document. `decodeValue` gives the value again (EXT.ROUNDTRIP).
6021
+ * @throws {TypeError} The value has a cycle, or it is not JSON and no domain with a codec holds it.
6022
+ */
6023
+ function encodeValue(machine, value) {
6024
+ const domains = machine.domains;
6025
+ const codecs = [];
6026
+ if (domains !== void 0) for (const name of Object.keys(domains).sort()) {
6027
+ const domain = domainOf(name, domains);
6028
+ const codec = domain === void 0 ? void 0 : codecOf(domain);
6029
+ if (codec !== void 0) codecs.push([name, codec]);
6030
+ }
6031
+ return encodeTerm(value, {
6032
+ domains,
6033
+ codecs,
6034
+ json: /* @__PURE__ */ new WeakMap(),
6035
+ open: /* @__PURE__ */ new Set()
6036
+ });
6037
+ }
6038
+ /**
6039
+ * This function reads a document of `encodeValue` again: it evaluates the document as a term, in an empty context.
6040
+ * A bare root value is a literal.
6041
+ *
6042
+ * @param machine - A machine of the formula kit, with the domains of the document.
6043
+ * @param doc - The document.
6044
+ * @returns The value, or the error of the evaluation.
6045
+ */
6046
+ function decodeValue(machine, doc) {
6047
+ return prepare(machine, doc, { declaredType: "lit" }).evaluate(makeContext());
6048
+ }
6049
+ //#endregion
5876
6050
  //#region src/formula.ts
5877
6051
  /** The mark of a field that the profile leaves out of a record. */
5878
6052
  const SKIPPED = /* @__PURE__ */ Symbol("skipped");
@@ -5893,7 +6067,10 @@ const CLOSURE$2 = /* @__PURE__ */ Symbol.for("coeval.closure");
5893
6067
  function isClosure(value) {
5894
6068
  return typeof value === "function" && value[CLOSURE$2] !== void 0;
5895
6069
  }
5896
- /** The standard extensions: the values that JSON cannot write. */
6070
+ /**
6071
+ * The standard extensions: the values that JSON cannot write. `ext.domain` reads a member of a domain through the
6072
+ * codec of the domain (EXT.DOMAIN, D40).
6073
+ */
5897
6074
  const STD_EXT = {
5898
6075
  undefined: () => ok(void 0),
5899
6076
  number: (data) => {
@@ -5902,7 +6079,8 @@ const STD_EXT = {
5902
6079
  if (data === "-Infinity") return ok(Number.NEGATIVE_INFINITY);
5903
6080
  if (data === "-0") return ok(-0);
5904
6081
  return fail("#VALUE!", "The value of ext.number is NaN, Infinity, -Infinity or -0.");
5905
- }
6082
+ },
6083
+ domain: (data, _context, domains) => decodeDomain(data, domains)
5906
6084
  };
5907
6085
  /** The truth of a value in the rules of JavaScript: `0`, `""`, `null`, `undefined` and `NaN` are false. */
5908
6086
  function jsTruthy(value) {
@@ -6369,8 +6547,9 @@ function formulaTypes(profile) {
6369
6547
  },
6370
6548
  "formula.ext": { synth: (input) => {
6371
6549
  const name = nameOf(input);
6372
- const own = profile.ext !== void 0 && Object.hasOwn(profile.ext, name);
6373
- return name === "number" && !own ? "number" : "unknown";
6550
+ if (profile.ext !== void 0 && Object.hasOwn(profile.ext, name)) return "unknown";
6551
+ if (name === "number") return "number";
6552
+ return name === "domain" ? domainTypeOf(input.doc.value, input.machine.domains) : "unknown";
6374
6553
  } }
6375
6554
  };
6376
6555
  }
@@ -6688,7 +6867,7 @@ function formulaCode(profile) {
6688
6867
  }
6689
6868
  };
6690
6869
  }),
6691
- "formula.ext": compiled(({ typePath, doc }) => {
6870
+ "formula.ext": compiled(({ typePath, doc, machine }) => {
6692
6871
  const name = typePath.slice(1).join(".");
6693
6872
  const handler = (profile.ext !== void 0 && Object.hasOwn(profile.ext, name) ? profile.ext[name] : void 0) ?? (Object.hasOwn(STD_EXT, name) ? STD_EXT[name] : void 0);
6694
6873
  if (handler === void 0) {
@@ -6697,7 +6876,7 @@ function formulaCode(profile) {
6697
6876
  }
6698
6877
  return (ctx) => {
6699
6878
  try {
6700
- return handler(doc.value, ctx);
6879
+ return handler(doc.value, ctx, machine.domains);
6701
6880
  } catch (error) {
6702
6881
  return thrown(error, `the extension ${name}`);
6703
6882
  }
@@ -6798,6 +6977,835 @@ function formulaMachine(profile = {}, more = {}) {
6798
6977
  }, more);
6799
6978
  }
6800
6979
  //#endregion
6980
+ //#region src/syntax.ts
6981
+ /**
6982
+ * The standard syntax of the formula kit. It gives the site and the playground a text form. The operators are
6983
+ * `or`, `and`, `not`, the comparisons, `+ - * / %`, a minus sign and `^`, from the loosest to the tightest. A call
6984
+ * `name(args)` applies the operation `ops.name`. `if(test, then, else)` and `list(items)` make the constructs.
6985
+ */
6986
+ const STD_SYNTAX = /* @__PURE__ */ Object.freeze({
6987
+ operators: [
6988
+ {
6989
+ symbol: "or",
6990
+ fixity: "infix",
6991
+ prec: 1,
6992
+ assoc: "flat",
6993
+ kind: "or"
6994
+ },
6995
+ {
6996
+ symbol: "and",
6997
+ fixity: "infix",
6998
+ prec: 2,
6999
+ assoc: "flat",
7000
+ kind: "and"
7001
+ },
7002
+ {
7003
+ symbol: "not",
7004
+ fixity: "prefix",
7005
+ prec: 3,
7006
+ kind: "ops.not"
7007
+ },
7008
+ {
7009
+ symbol: "==",
7010
+ fixity: "infix",
7011
+ prec: 4,
7012
+ assoc: "none",
7013
+ kind: "ops.eq"
7014
+ },
7015
+ {
7016
+ symbol: "!=",
7017
+ fixity: "infix",
7018
+ prec: 4,
7019
+ assoc: "none",
7020
+ kind: "ops.ne"
7021
+ },
7022
+ {
7023
+ symbol: "<",
7024
+ fixity: "infix",
7025
+ prec: 4,
7026
+ assoc: "none",
7027
+ kind: "ops.lt"
7028
+ },
7029
+ {
7030
+ symbol: "<=",
7031
+ fixity: "infix",
7032
+ prec: 4,
7033
+ assoc: "none",
7034
+ kind: "ops.le"
7035
+ },
7036
+ {
7037
+ symbol: ">",
7038
+ fixity: "infix",
7039
+ prec: 4,
7040
+ assoc: "none",
7041
+ kind: "ops.gt"
7042
+ },
7043
+ {
7044
+ symbol: ">=",
7045
+ fixity: "infix",
7046
+ prec: 4,
7047
+ assoc: "none",
7048
+ kind: "ops.ge"
7049
+ },
7050
+ {
7051
+ symbol: "+",
7052
+ fixity: "infix",
7053
+ prec: 5,
7054
+ kind: "ops.add"
7055
+ },
7056
+ {
7057
+ symbol: "-",
7058
+ fixity: "infix",
7059
+ prec: 5,
7060
+ kind: "ops.sub"
7061
+ },
7062
+ {
7063
+ symbol: "*",
7064
+ fixity: "infix",
7065
+ prec: 6,
7066
+ kind: "ops.mul"
7067
+ },
7068
+ {
7069
+ symbol: "/",
7070
+ fixity: "infix",
7071
+ prec: 6,
7072
+ kind: "ops.div"
7073
+ },
7074
+ {
7075
+ symbol: "%",
7076
+ fixity: "infix",
7077
+ prec: 6,
7078
+ kind: "ops.mod"
7079
+ },
7080
+ {
7081
+ symbol: "-",
7082
+ fixity: "prefix",
7083
+ prec: 7,
7084
+ kind: "ops.neg"
7085
+ },
7086
+ {
7087
+ symbol: "^",
7088
+ fixity: "infix",
7089
+ prec: 8,
7090
+ assoc: "right",
7091
+ kind: "ops.pow"
7092
+ }
7093
+ ],
7094
+ literals: { words: {
7095
+ true: true,
7096
+ false: false,
7097
+ null: null,
7098
+ undefined: void 0,
7099
+ NaN: NaN,
7100
+ Infinity: Infinity
7101
+ } },
7102
+ calls: { forms: [{
7103
+ name: "if",
7104
+ kind: "if",
7105
+ fields: [
7106
+ "test",
7107
+ "then",
7108
+ "else"
7109
+ ],
7110
+ min: 2
7111
+ }, {
7112
+ name: "list",
7113
+ kind: "list"
7114
+ }] }
7115
+ });
7116
+ const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
7117
+ const NAME_AT = /[A-Za-z_][A-Za-z0-9_]*/y;
7118
+ const DIGITS_AT = /[0-9]+/y;
7119
+ const NUMBER_AT = /(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
7120
+ const SYMBOL = /^[^\sA-Za-z0-9_"'`()$.]+$/;
7121
+ const DEFAULT_WORDS = {
7122
+ true: true,
7123
+ false: false,
7124
+ null: null
7125
+ };
7126
+ const DEFAULT_ROOTS = [
7127
+ "self",
7128
+ "world",
7129
+ "event",
7130
+ "caps"
7131
+ ];
7132
+ const compiledProfiles = /* @__PURE__ */ new WeakMap();
7133
+ function typeOfKind(kind) {
7134
+ const keys = kind.split(".");
7135
+ if (kind === "" || keys.some((key) => key === "")) throw new TypeError(`The kind "${kind}" of a syntax profile has an empty key.`);
7136
+ return {
7137
+ type: keys.length === 1 ? keys[0] : keys,
7138
+ key: JSON.stringify(keys)
7139
+ };
7140
+ }
7141
+ function addTo(map, key, value) {
7142
+ const list = map.get(key);
7143
+ if (list === void 0) map.set(key, [value]);
7144
+ else list.push(value);
7145
+ }
7146
+ function compileOps(profile, separator) {
7147
+ const prefix = /* @__PURE__ */ new Map();
7148
+ const binary = /* @__PURE__ */ new Map();
7149
+ const byKind = /* @__PURE__ */ new Map();
7150
+ const assocAt = /* @__PURE__ */ new Map();
7151
+ const opWords = /* @__PURE__ */ new Set();
7152
+ for (const given of profile.operators ?? []) {
7153
+ const word = NAME.test(given.symbol);
7154
+ if (!word && (!SYMBOL.test(given.symbol) || given.symbol.includes(separator))) throw new TypeError(`The operator "${given.symbol}" is not a plain name or a group of symbols.`);
7155
+ if (!Number.isFinite(given.prec)) throw new TypeError(`The operator "${given.symbol}" has no finite precedence.`);
7156
+ const { type, key } = typeOfKind(given.kind);
7157
+ const op = {
7158
+ symbol: given.symbol,
7159
+ fixity: given.fixity,
7160
+ prec: given.prec,
7161
+ assoc: given.assoc ?? "left",
7162
+ type,
7163
+ kindKey: key,
7164
+ word
7165
+ };
7166
+ const table = op.fixity === "prefix" ? prefix : binary;
7167
+ if (table.has(op.symbol)) throw new TypeError(`The operator "${op.symbol}" has two entries of the same place.`);
7168
+ table.set(op.symbol, op);
7169
+ if (op.fixity === "infix") {
7170
+ const assoc = assocAt.get(op.prec);
7171
+ if (assoc !== void 0 && assoc !== op.assoc) throw new TypeError(`The infix operators of the precedence ${String(op.prec)} have different groupings.`);
7172
+ assocAt.set(op.prec, op.assoc);
7173
+ }
7174
+ if (word) opWords.add(op.symbol);
7175
+ addTo(byKind, key, op);
7176
+ }
7177
+ const longestFirst = (table) => [...table.keys()].filter((symbol) => !NAME.test(symbol)).sort((a, b) => b.length - a.length);
7178
+ return {
7179
+ prefix,
7180
+ binary,
7181
+ prefixSymbols: longestFirst(prefix),
7182
+ binarySymbols: longestFirst(binary),
7183
+ byKind,
7184
+ opWords
7185
+ };
7186
+ }
7187
+ function compileProfile(profile) {
7188
+ const cached = compiledProfiles.get(profile);
7189
+ if (cached !== void 0) return cached;
7190
+ const calls = profile.calls ?? {};
7191
+ const separator = calls.separator ?? ",";
7192
+ const ops = compileOps(profile, separator);
7193
+ const literals = profile.literals ?? {};
7194
+ const strings = literals.strings === false ? void 0 : literals.strings ?? {};
7195
+ const words = new Map(Object.entries(literals.words ?? DEFAULT_WORDS));
7196
+ const refs = profile.refs ?? {};
7197
+ const roots = new Set(refs.roots ?? DEFAULT_ROOTS);
7198
+ const forms = /* @__PURE__ */ new Map();
7199
+ const formsByKind = /* @__PURE__ */ new Map();
7200
+ for (const given of calls.forms ?? []) {
7201
+ if (!NAME.test(given.name)) throw new TypeError(`The call form "${given.name}" is not a plain name.`);
7202
+ const { type, key } = typeOfKind(given.kind);
7203
+ const form = {
7204
+ name: given.name,
7205
+ type,
7206
+ fields: given.fields,
7207
+ min: given.min ?? given.fields?.length ?? 0
7208
+ };
7209
+ forms.set(form.name, form);
7210
+ addTo(formsByKind, key, form);
7211
+ }
7212
+ const compiled = {
7213
+ ...ops,
7214
+ numbers: literals.numbers ?? true,
7215
+ signed: literals.signed ?? true,
7216
+ quote: strings === void 0 ? void 0 : strings.quote ?? "\"",
7217
+ doubled: strings !== void 0 && strings.escape === "double",
7218
+ words,
7219
+ bare: refs.bare ?? "names",
7220
+ roots,
7221
+ fields: refs.fields ?? true,
7222
+ patterns: (refs.patterns ?? []).map((p) => {
7223
+ const flags = p.pattern.flags.replace(/[gy]/g, "");
7224
+ return {
7225
+ sticky: new RegExp(p.pattern.source, `${flags}y`),
7226
+ whole: new RegExp(`^(?:${p.pattern.source})$`, flags),
7227
+ read: p.read,
7228
+ write: p.write
7229
+ };
7230
+ }),
7231
+ ops: calls.ops === void 0 || calls.ops === true ? true : new Set(calls.ops === false ? [] : calls.ops),
7232
+ forms,
7233
+ formsByKind,
7234
+ separator,
7235
+ reserved: /* @__PURE__ */ new Set([
7236
+ ...words.keys(),
7237
+ ...ops.opWords,
7238
+ ...roots
7239
+ ]),
7240
+ maxDepth: profile.maxDepth ?? 256
7241
+ };
7242
+ compiledProfiles.set(profile, compiled);
7243
+ return compiled;
7244
+ }
7245
+ /** The stop of a parse. The parser throws it, and `parse` gives its issue. */
7246
+ var Stop = class {
7247
+ issue;
7248
+ constructor(issue) {
7249
+ this.issue = issue;
7250
+ }
7251
+ };
7252
+ function literalDoc(value) {
7253
+ return encodeValue({}, value);
7254
+ }
7255
+ function opDoc(op, value) {
7256
+ return {
7257
+ type: op.type,
7258
+ value
7259
+ };
7260
+ }
7261
+ const ESCAPES = {
7262
+ "\"": "\"",
7263
+ "'": "'",
7264
+ "\\": "\\",
7265
+ "/": "/",
7266
+ b: "\b",
7267
+ f: "\f",
7268
+ n: "\n",
7269
+ r: "\r",
7270
+ t: " "
7271
+ };
7272
+ var Parser = class {
7273
+ syn;
7274
+ text;
7275
+ pos = 0;
7276
+ depth = 0;
7277
+ constructor(syn, text) {
7278
+ this.syn = syn;
7279
+ this.text = text;
7280
+ }
7281
+ parse() {
7282
+ this.skip();
7283
+ if (this.pos >= this.text.length) this.stop("The text is empty.", 0, 0);
7284
+ const doc = this.expr(0);
7285
+ this.skip();
7286
+ if (this.pos < this.text.length) this.stop("The text continues after the expression.", this.pos, this.tokenLength());
7287
+ return doc;
7288
+ }
7289
+ position() {
7290
+ return this.pos;
7291
+ }
7292
+ stop(message, offset, length) {
7293
+ throw new Stop({
7294
+ offset,
7295
+ length,
7296
+ message
7297
+ });
7298
+ }
7299
+ skip() {
7300
+ while (this.pos < this.text.length && /\s/.test(this.text[this.pos])) this.pos += 1;
7301
+ }
7302
+ at(pattern, pos = this.pos) {
7303
+ pattern.lastIndex = pos;
7304
+ return pattern.exec(this.text)?.[0];
7305
+ }
7306
+ tokenLength() {
7307
+ return (this.at(NAME_AT) ?? this.at(DIGITS_AT) ?? "x").length;
7308
+ }
7309
+ /** This method reads an expression by precedence climbing. It stops at an operator below `min`. */
7310
+ expr(min) {
7311
+ const start = this.pos;
7312
+ this.depth += 1;
7313
+ if (this.depth > this.syn.maxDepth) this.stop(`The text is deeper than ${String(this.syn.maxDepth)} levels.`, start, 0);
7314
+ let left = this.operand();
7315
+ for (;;) {
7316
+ this.skip();
7317
+ const found = this.operatorAt();
7318
+ if (found === void 0 || found.op.prec < min) break;
7319
+ const op = found.op;
7320
+ this.pos += found.length;
7321
+ if (op.fixity === "postfix") {
7322
+ left = opDoc(op, [left]);
7323
+ continue;
7324
+ }
7325
+ if (op.assoc === "flat") {
7326
+ const terms = [left, this.expr(op.prec + 1)];
7327
+ for (;;) {
7328
+ this.skip();
7329
+ const next = this.operatorAt();
7330
+ if (next === void 0 || next.op.fixity !== "infix" || next.op.kindKey !== op.kindKey) break;
7331
+ this.pos += next.length;
7332
+ terms.push(this.expr(op.prec + 1));
7333
+ }
7334
+ left = opDoc(op, terms);
7335
+ continue;
7336
+ }
7337
+ left = opDoc(op, [left, this.expr(op.assoc === "right" ? op.prec : op.prec + 1)]);
7338
+ if (op.assoc === "none") {
7339
+ this.skip();
7340
+ const next = this.operatorAt();
7341
+ if (next !== void 0 && next.op.fixity === "infix" && next.op.prec === op.prec) this.stop(`The operator ${next.op.symbol} does not chain after ${op.symbol}. Add parentheses.`, this.pos, next.length);
7342
+ }
7343
+ }
7344
+ this.depth -= 1;
7345
+ return left;
7346
+ }
7347
+ /** This method finds an infix or a postfix operator at the position. */
7348
+ operatorAt() {
7349
+ const name = this.at(NAME_AT);
7350
+ if (name !== void 0) {
7351
+ const op = this.syn.binary.get(name);
7352
+ return op !== void 0 && op.word ? {
7353
+ op,
7354
+ length: name.length
7355
+ } : void 0;
7356
+ }
7357
+ for (const symbol of this.syn.binarySymbols) if (this.text.startsWith(symbol, this.pos)) return {
7358
+ op: this.syn.binary.get(symbol),
7359
+ length: symbol.length
7360
+ };
7361
+ }
7362
+ operand() {
7363
+ this.skip();
7364
+ const start = this.pos;
7365
+ const c = this.text[start];
7366
+ if (c === void 0) return this.stop("A term is missing at the end of the text.", start, 0);
7367
+ for (const pattern of this.syn.patterns) {
7368
+ const match = this.at(pattern.sticky);
7369
+ if (match !== void 0 && match !== "" && !/[A-Za-z0-9_$(]/.test(this.text[start + match.length] ?? "")) {
7370
+ this.pos += match.length;
7371
+ return {
7372
+ type: "ref",
7373
+ value: pattern.read(match)
7374
+ };
7375
+ }
7376
+ }
7377
+ if (/[0-9]/.test(c)) return this.number(false);
7378
+ if (c === "-" && this.syn.signed && /[0-9]/.test(this.text[start + 1] ?? "")) return this.number(true);
7379
+ if (c === this.syn.quote) return this.string();
7380
+ if (c === "(") return this.group();
7381
+ if (c === "`" || /[A-Za-z_]/.test(c)) return this.named();
7382
+ for (const symbol of this.syn.prefixSymbols) if (this.text.startsWith(symbol, start)) {
7383
+ this.pos += symbol.length;
7384
+ const op = this.syn.prefix.get(symbol);
7385
+ return opDoc(op, [this.expr(op.prec + 1)]);
7386
+ }
7387
+ if (c === ")" || this.text.startsWith(this.syn.separator, start)) return this.stop("A term is missing.", start, 0);
7388
+ return this.stop(`The text "${c}" cannot start a term.`, start, 1);
7389
+ }
7390
+ number(negative) {
7391
+ const start = this.pos;
7392
+ if (!this.syn.numbers) this.stop("This syntax has no numbers.", start, this.tokenLength());
7393
+ if (negative) this.pos += 1;
7394
+ const digits = this.at(NUMBER_AT);
7395
+ this.pos += digits.length;
7396
+ const value = Number(digits);
7397
+ return literalDoc(negative ? -value : value);
7398
+ }
7399
+ string() {
7400
+ const start = this.pos;
7401
+ const quote = this.syn.quote;
7402
+ let out = "";
7403
+ this.pos += 1;
7404
+ for (;;) {
7405
+ const c = this.text[this.pos];
7406
+ if (c === void 0) return this.stop("A closing quote is missing.", start, this.text.length - start);
7407
+ if (c === quote) {
7408
+ if (this.syn.doubled && this.text[this.pos + 1] === quote) {
7409
+ out += quote;
7410
+ this.pos += 2;
7411
+ continue;
7412
+ }
7413
+ this.pos += 1;
7414
+ return literalDoc(out);
7415
+ }
7416
+ if (c === "\\" && !this.syn.doubled) {
7417
+ out += this.escape();
7418
+ continue;
7419
+ }
7420
+ out += c;
7421
+ this.pos += 1;
7422
+ }
7423
+ }
7424
+ escape() {
7425
+ const start = this.pos;
7426
+ const c = this.text[start + 1] ?? "";
7427
+ if (c === "u") {
7428
+ const hex = this.text.slice(start + 2, start + 6);
7429
+ if (!/^[0-9A-Fa-f]{4}$/.test(hex)) return this.stop("The escape \\u needs four hex digits.", start, 2);
7430
+ this.pos += 6;
7431
+ return String.fromCharCode(Number.parseInt(hex, 16));
7432
+ }
7433
+ const out = Object.hasOwn(ESCAPES, c) ? ESCAPES[c] : void 0;
7434
+ if (out === void 0) return this.stop(`The escape \\${c} is not valid.`, start, 2);
7435
+ this.pos += 2;
7436
+ return out;
7437
+ }
7438
+ group() {
7439
+ const open = this.pos;
7440
+ this.pos += 1;
7441
+ const inner = this.expr(0);
7442
+ this.skip();
7443
+ if (this.text[this.pos] !== ")") {
7444
+ if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
7445
+ this.stop("A closing parenthesis is missing.", this.pos, this.tokenLength());
7446
+ }
7447
+ this.pos += 1;
7448
+ return inner;
7449
+ }
7450
+ /** This method reads a name in backticks. Two backticks in the name are one backtick. */
7451
+ quoted() {
7452
+ const start = this.pos;
7453
+ let out = "";
7454
+ this.pos += 1;
7455
+ for (;;) {
7456
+ const c = this.text[this.pos];
7457
+ if (c === void 0) return this.stop("A closing backtick is missing.", start, this.text.length - start);
7458
+ if (c === "`") {
7459
+ if (this.text[this.pos + 1] !== "`") {
7460
+ this.pos += 1;
7461
+ return out;
7462
+ }
7463
+ this.pos += 1;
7464
+ }
7465
+ out += c;
7466
+ this.pos += 1;
7467
+ }
7468
+ }
7469
+ /** This method reads a name: a word operator, a call, a literal word or a reference. */
7470
+ named() {
7471
+ const start = this.pos;
7472
+ const plain = this.text[start] !== "`";
7473
+ const first = plain ? this.at(NAME_AT) : this.quoted();
7474
+ if (plain) {
7475
+ this.pos += first.length;
7476
+ const prefix = this.syn.prefix.get(first);
7477
+ if (prefix !== void 0 && prefix.word) return opDoc(prefix, [this.expr(prefix.prec + 1)]);
7478
+ if (this.syn.binary.get(first)?.word === true) this.stop(`A term is missing before ${first}.`, start, first.length);
7479
+ }
7480
+ const path = [first];
7481
+ let allPlain = plain;
7482
+ for (;;) {
7483
+ const back = this.pos;
7484
+ this.skip();
7485
+ if (!this.syn.fields || this.text[this.pos] !== ".") {
7486
+ this.pos = back;
7487
+ break;
7488
+ }
7489
+ this.pos += 1;
7490
+ this.skip();
7491
+ if (this.text[this.pos] === "`") {
7492
+ path.push(this.quoted());
7493
+ allPlain = false;
7494
+ continue;
7495
+ }
7496
+ const name = this.at(NAME_AT);
7497
+ const digits = name === void 0 ? this.at(DIGITS_AT) : void 0;
7498
+ if (name === void 0 && digits === void 0) this.stop("A name is missing after the dot.", this.pos, 0);
7499
+ this.pos += (name ?? digits).length;
7500
+ path.push(name ?? Number(digits));
7501
+ if (name === void 0) allPlain = false;
7502
+ }
7503
+ const back = this.pos;
7504
+ this.skip();
7505
+ if (this.text[this.pos] === "(") {
7506
+ if (!allPlain) this.stop("The name of a call is a plain name.", start, back - start);
7507
+ return this.call(path.join("."), start, back - start);
7508
+ }
7509
+ this.pos = back;
7510
+ if (plain && path.length === 1 && this.syn.words.has(first)) return literalDoc(this.syn.words.get(first));
7511
+ return {
7512
+ type: "ref",
7513
+ value: plain && this.syn.roots.has(first) ? path : [this.syn.bare, ...path]
7514
+ };
7515
+ }
7516
+ call(name, start, nameLength) {
7517
+ const open = this.pos;
7518
+ const args = [];
7519
+ this.pos += 1;
7520
+ this.skip();
7521
+ if (this.text[this.pos] === ")") this.pos += 1;
7522
+ else for (;;) {
7523
+ args.push(this.expr(0));
7524
+ this.skip();
7525
+ if (this.text.startsWith(this.syn.separator, this.pos)) {
7526
+ this.pos += this.syn.separator.length;
7527
+ continue;
7528
+ }
7529
+ if (this.text[this.pos] === ")") {
7530
+ this.pos += 1;
7531
+ break;
7532
+ }
7533
+ if (this.pos >= this.text.length) this.stop("A closing parenthesis is missing.", open, 1);
7534
+ this.stop(`A "${this.syn.separator}" or a closing parenthesis is missing.`, this.pos, this.tokenLength());
7535
+ }
7536
+ const form = this.syn.forms.get(name);
7537
+ if (form !== void 0) {
7538
+ const most = form.fields?.length ?? Number.POSITIVE_INFINITY;
7539
+ if (args.length < form.min || args.length > most) {
7540
+ const range = most === form.min ? String(most) : most === Number.POSITIVE_INFINITY ? `${String(form.min)} or more` : `${String(form.min)} to ${String(most)}`;
7541
+ this.stop(`The form ${name} takes ${range} arguments.`, start, this.pos - start);
7542
+ }
7543
+ if (form.fields === void 0) return opDoc(form, args);
7544
+ const value = {};
7545
+ args.forEach((arg, index) => {
7546
+ value[form.fields?.[index]] = arg;
7547
+ });
7548
+ return opDoc(form, value);
7549
+ }
7550
+ if (this.syn.ops !== true && !this.syn.ops.has(name)) this.stop(`This syntax has no function ${name}.`, start, nameLength);
7551
+ return {
7552
+ type: ["ops", ...name.split(".")],
7553
+ value: args
7554
+ };
7555
+ }
7556
+ };
7557
+ /**
7558
+ * This function reads a text into a document with the rules of a profile (SYNTAX.PARSE). It does not throw for a
7559
+ * bad text. The issue gives the offset, the length and a message (SYNTAX.SPAN).
7560
+ *
7561
+ * @param profile - The syntax profile.
7562
+ * @param text - The text.
7563
+ * @returns The document, or the issues of the text.
7564
+ * @throws {TypeError} The profile is not valid, for example an operator has two entries of the same place.
7565
+ */
7566
+ function parse(profile, text) {
7567
+ const parser = new Parser(compileProfile(profile), text);
7568
+ try {
7569
+ return {
7570
+ ok: true,
7571
+ doc: parser.parse()
7572
+ };
7573
+ } catch (error) {
7574
+ if (error instanceof Stop) return {
7575
+ ok: false,
7576
+ issues: [error.issue]
7577
+ };
7578
+ if (isStackOverflow(error)) return {
7579
+ ok: false,
7580
+ issues: [{
7581
+ offset: parser.position(),
7582
+ length: 0,
7583
+ message: "The text is too deep for the stack of the host."
7584
+ }]
7585
+ };
7586
+ throw error;
7587
+ }
7588
+ }
7589
+ function none(reason) {
7590
+ return {
7591
+ tag: "none",
7592
+ reason
7593
+ };
7594
+ }
7595
+ function quoteName(name) {
7596
+ return `\`${name.replaceAll("`", "``")}\``;
7597
+ }
7598
+ function wordFor(syn, value) {
7599
+ for (const [word, held] of syn.words) if (Object.is(held, value)) return word;
7600
+ }
7601
+ function textOfString(syn, value) {
7602
+ const quote = syn.quote;
7603
+ if (quote === void 0) return void 0;
7604
+ if (syn.doubled) return `${quote}${value.replaceAll(quote, quote + quote)}${quote}`;
7605
+ const json = JSON.stringify(value);
7606
+ return quote === "\"" ? json : `'${json.slice(1, -1).replaceAll("\\\"", "\"").replaceAll("'", "\\'")}'`;
7607
+ }
7608
+ function textOfNumber(syn, value) {
7609
+ if (!syn.numbers) return wordFor(syn, value);
7610
+ const negative = value < 0 || Object.is(value, -0);
7611
+ if (negative && !syn.signed) return wordFor(syn, value);
7612
+ if (Number.isFinite(value)) return negative ? `-${String(-value)}` : String(value);
7613
+ return wordFor(syn, value) ?? (Number.isNaN(value) ? void 0 : negative ? "-1e999" : "1e999");
7614
+ }
7615
+ function literalShape(syn, value) {
7616
+ const text = typeof value === "string" ? textOfString(syn, value) : typeof value === "number" ? textOfNumber(syn, value) : wordFor(syn, value);
7617
+ return text === void 0 ? none(`This syntax has no text for the literal ${describe(value)}.`) : {
7618
+ tag: "text",
7619
+ text
7620
+ };
7621
+ }
7622
+ function plainSegment(syn, name) {
7623
+ return NAME.test(name) && !syn.reserved.has(name) && !syn.patterns.some((p) => p.whole.test(name));
7624
+ }
7625
+ function laterSegment(step) {
7626
+ if (typeof step === "string") return NAME.test(step) ? step : quoteName(step);
7627
+ return typeof step === "number" && Number.isSafeInteger(step) && step >= 0 ? String(step) : void 0;
7628
+ }
7629
+ function refShape(syn, value) {
7630
+ if (!Array.isArray(value) || value.length === 0) return none("The path of the reference is empty.");
7631
+ for (const pattern of syn.patterns) {
7632
+ const text = pattern.write(value);
7633
+ if (text !== void 0) return {
7634
+ tag: "text",
7635
+ text
7636
+ };
7637
+ }
7638
+ const [root, ...rest] = value;
7639
+ if ((rest.length > 1 || root !== syn.bare && rest.length > 0) && !syn.fields) return none("This syntax has no field steps.");
7640
+ const later = rest.slice(1).map(laterSegment);
7641
+ let first;
7642
+ let tail = later;
7643
+ if (root === syn.bare && typeof rest[0] === "string") first = plainSegment(syn, rest[0]) ? rest[0] : quoteName(rest[0]);
7644
+ else if (typeof root === "string" && syn.roots.has(root) && !syn.patterns.some((p) => p.whole.test(root))) {
7645
+ first = root;
7646
+ tail = rest.map(laterSegment);
7647
+ }
7648
+ if (first === void 0 || tail.includes(void 0)) return none("This syntax has no text for the path of the reference.");
7649
+ return {
7650
+ tag: "text",
7651
+ text: [first, ...tail].join(".")
7652
+ };
7653
+ }
7654
+ function callName(syn, keys) {
7655
+ if (keys.length === 0 || !keys.every((key) => typeof key === "string" && NAME.test(key))) return void 0;
7656
+ const name = keys.join(".");
7657
+ if (syn.forms.has(name) || syn.opWords.has(keys[0])) return void 0;
7658
+ return syn.ops === true || syn.ops.has(name) ? name : void 0;
7659
+ }
7660
+ function formShape(form, value) {
7661
+ if (form.fields === void 0) {
7662
+ if (!Array.isArray(value) || value.length < form.min) return void 0;
7663
+ return {
7664
+ tag: "call",
7665
+ name: form.name,
7666
+ args: value.map((item, index) => [index, item])
7667
+ };
7668
+ }
7669
+ if (!isRecord(value)) return void 0;
7670
+ const count = Object.keys(value).length;
7671
+ const present = form.fields.slice(0, count);
7672
+ if (count < form.min || present.length < count || !present.every((key) => Object.hasOwn(value, key))) return void 0;
7673
+ return {
7674
+ tag: "call",
7675
+ name: form.name,
7676
+ args: present.map((key) => [key, value[key]])
7677
+ };
7678
+ }
7679
+ function opShape(syn, key, terms) {
7680
+ for (const op of syn.byKind.get(key) ?? []) if (op.fixity === "infix" ? terms.length === 2 || op.assoc === "flat" && terms.length > 2 : terms.length === 1) return {
7681
+ tag: "op",
7682
+ op,
7683
+ terms
7684
+ };
7685
+ }
7686
+ /** This function gives the shape of a document: a literal or a reference, an operator, a call, or no text. */
7687
+ function shapeOf(syn, doc) {
7688
+ if (!isEnvelope(doc)) return Array.isArray(doc) || isRecord(doc) ? none("This syntax has no text for a list or a record literal.") : literalShape(syn, doc);
7689
+ if (doc.name !== void 0 || doc.own !== void 0) return none("This syntax has no text for the header of a document.");
7690
+ const path = typePath(doc.type);
7691
+ if (path === void 0) return none("This syntax has no text for a computed type.");
7692
+ const value = doc.value;
7693
+ const name = path.join(".");
7694
+ if (name === "lit") return isEnvelope(value) || Array.isArray(value) || isRecord(value) ? none("This syntax has no text for a list or a record literal.") : literalShape(syn, value);
7695
+ if (name === "ext.undefined") return literalShape(syn, void 0);
7696
+ if (name === "ext.number") {
7697
+ const numbers = {
7698
+ NaN: NaN,
7699
+ Infinity: Number.POSITIVE_INFINITY,
7700
+ "-Infinity": Number.NEGATIVE_INFINITY,
7701
+ "-0": -0
7702
+ };
7703
+ return typeof value === "string" && Object.hasOwn(numbers, value) ? literalShape(syn, numbers[value]) : none("The value of ext.number is not valid.");
7704
+ }
7705
+ if (name === "ref") return refShape(syn, value);
7706
+ const key = JSON.stringify(path);
7707
+ const op = Array.isArray(value) ? opShape(syn, key, value) : void 0;
7708
+ if (op !== void 0) return op;
7709
+ for (const form of syn.formsByKind.get(key) ?? []) {
7710
+ const shape = formShape(form, value);
7711
+ if (shape !== void 0) return shape;
7712
+ }
7713
+ const called = path[0] === "ops" && Array.isArray(value) ? callName(syn, path.slice(1)) : void 0;
7714
+ if (called !== void 0) return {
7715
+ tag: "call",
7716
+ name: called,
7717
+ args: value.map((item, index) => [index, item])
7718
+ };
7719
+ return none(`This syntax has no text for the kind ${name}.`);
7720
+ }
7721
+ function levelOf(syn, doc) {
7722
+ const shape = shapeOf(syn, doc);
7723
+ return shape.tag === "op" ? shape.op.prec : Number.POSITIVE_INFINITY;
7724
+ }
7725
+ /** This function tells if a term needs parentheses in its position, so that `parse` reads the same tree. */
7726
+ function needsGroup(op, index, level) {
7727
+ if (op.fixity === "postfix") return level < op.prec;
7728
+ if (op.fixity === "prefix" || op.assoc === "none" || op.assoc === "flat") return level <= op.prec;
7729
+ return (op.assoc === "left" ? index === 0 : index !== 0) ? level < op.prec : level <= op.prec;
7730
+ }
7731
+ function opText(syn, op, terms, sub) {
7732
+ const texts = [];
7733
+ for (let index = 0; index < terms.length; index += 1) {
7734
+ const term = terms[index];
7735
+ const result = sub(term, [index]);
7736
+ if (!result.ok) return result;
7737
+ let text = result.value;
7738
+ const signedDigit = op.fixity === "prefix" && op.symbol === "-" && syn.signed && /^[0-9]/.test(text);
7739
+ if (needsGroup(op, index, levelOf(syn, term)) || signedDigit) text = `(${text})`;
7740
+ texts.push(text);
7741
+ }
7742
+ const [only = ""] = texts;
7743
+ if (op.fixity === "prefix") return ok(op.word || /^[^A-Za-z0-9_("'`$]/.test(only) ? `${op.symbol} ${only}` : `${op.symbol}${only}`);
7744
+ if (op.fixity === "postfix") return ok(op.word ? `${only} ${op.symbol}` : `${only}${op.symbol}`);
7745
+ return ok(texts.join(` ${op.symbol} `));
7746
+ }
7747
+ /** This function writes one document. `sub` gives the text of each part. */
7748
+ function printNode(syn, doc, sub) {
7749
+ const shape = shapeOf(syn, doc);
7750
+ if (shape.tag === "text") return ok(shape.text);
7751
+ if (shape.tag === "none") return fail("#VALUE!", shape.reason);
7752
+ if (shape.tag === "op") return opText(syn, shape.op, shape.terms, sub);
7753
+ const texts = [];
7754
+ for (const [key, arg] of shape.args) {
7755
+ const result = sub(arg, [key]);
7756
+ if (!result.ok) return result;
7757
+ texts.push(result.value);
7758
+ }
7759
+ return ok(`${shape.name}(${texts.join(`${syn.separator} `)})`);
7760
+ }
7761
+ function printTree(syn, doc, place) {
7762
+ const result = printNode(syn, doc, (child, path) => printTree(syn, child, [...place, ...path]));
7763
+ return result.ok || result.error.place !== void 0 ? result : fail(result.error.code, result.error.reason, { place });
7764
+ }
7765
+ /**
7766
+ * This function writes a document as text with the rules of a profile (SYNTAX.PRINT). It writes the fewest
7767
+ * parentheses that keep the tree. `parse` reads the text back to the same document (SYNTAX.ROUNDTRIP).
7768
+ *
7769
+ * @param profile - The syntax profile.
7770
+ * @param doc - The document.
7771
+ * @returns The text, or `#VALUE!` at the place of a document that the profile cannot write.
7772
+ * @throws {TypeError} The profile is not valid.
7773
+ */
7774
+ function print(profile, doc) {
7775
+ const syn = compileProfile(profile);
7776
+ try {
7777
+ return printTree(syn, doc, []);
7778
+ } catch (error) {
7779
+ if (isStackOverflow(error)) return fail("#DEPTH!", "The document is too deep for the stack of the host.", { cause: error });
7780
+ throw error;
7781
+ }
7782
+ }
7783
+ /**
7784
+ * This function makes the part of a merge that adds the verb `format` to a machine (D23, D41). Its default writes
7785
+ * each document with the printer of the profile. A part gives its own text through the ladder, thus an own entry or
7786
+ * a kind entry of `format` still wins.
7787
+ *
7788
+ * @param profile - The syntax profile.
7789
+ * @returns The part for `mergeMachines`.
7790
+ */
7791
+ function syntaxFormat(profile) {
7792
+ const syn = compileProfile(profile);
7793
+ const code = ({ doc, parts, place }) => {
7794
+ const byPath = new Map(parts.map((part) => [JSON.stringify(part.path), part]));
7795
+ return printNode(syn, doc, (child, path) => {
7796
+ const part = byPath.get(JSON.stringify(path));
7797
+ if (part === void 0) return printTree(syn, child, [...place, ...path]);
7798
+ const result = part.evaluate();
7799
+ if (!result.ok) return result;
7800
+ return typeof result.value === "string" ? ok(result.value) : fail("#VALUE!", `The verb format of a part gave ${describe(result.value)}, not a text.`, { place: [...place, ...path] });
7801
+ });
7802
+ };
7803
+ return {
7804
+ world: { defaults: { format: "syntax.format" } },
7805
+ evaluators: { "syntax.format": code }
7806
+ };
7807
+ }
7808
+ //#endregion
6801
7809
  //#region src/reference.ts
6802
7810
  function own$1(table, name) {
6803
7811
  return table !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
@@ -7008,6 +8016,7 @@ function evalInput(env, seen, at) {
7008
8016
  parts,
7009
8017
  context: at.ctx,
7010
8018
  place: at.place,
8019
+ machine: env.machine,
7011
8020
  ...at.address ? { address: true } : {},
7012
8021
  resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
7013
8022
  effect: (kind, payload) => cause(env, at.ctx, kind, payload, at.place),
@@ -7035,6 +8044,7 @@ function runInput(env, seen, at) {
7035
8044
  parts,
7036
8045
  context: at.ctx,
7037
8046
  place: at.place,
8047
+ machine: env.machine,
7038
8048
  resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
7039
8049
  effect: function* (kind, payload) {
7040
8050
  return cause(env, at.ctx, kind, payload, at.place);
@@ -7437,12 +8447,12 @@ function formulaDefinitions(profile = {}) {
7437
8447
  return thrown(error, `the operation ${name}`);
7438
8448
  }
7439
8449
  };
7440
- const ext = ({ typePath, doc, context }) => {
8450
+ const ext = ({ typePath, doc, context, machine }) => {
7441
8451
  const name = typePath.slice(1).join(".");
7442
8452
  const handler = own$1(profile.ext, name) ?? own$1(STD_EXT, name);
7443
8453
  if (handler === void 0) return fail("#NAME?", `The language has no extension ${name}.`);
7444
8454
  try {
7445
- return handler(doc.value, context);
8455
+ return handler(doc.value, context, machine?.domains);
7446
8456
  } catch (error) {
7447
8457
  return thrown(error, `the extension ${name}`);
7448
8458
  }
@@ -8633,6 +9643,7 @@ function instanceOf(env, under, doc, split, sample) {
8633
9643
  doc,
8634
9644
  data: split.data,
8635
9645
  place: PLACE,
9646
+ machine: env.machine,
8636
9647
  fill
8637
9648
  };
8638
9649
  const who = `the ${env.runner ? "runner" : "evaluator"} ${env.name}`;
@@ -10244,6 +11255,6 @@ function typeFromSamples(values, options = {}) {
10244
11255
  return samplesAt(values, options.closed === true, 0);
10245
11256
  }
10246
11257
  //#endregion
10247
- export { CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkLatent, checkSteps, classify, components, conforms, context, cycles, dataEdges, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, entriesAsJson, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
11258
+ export { CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, agree, ask, capKeys, check, checkLatent, checkSteps, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, encodeValue, entriesAsJson, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placed, planner, prepare, print, random, recorder, ref, reference, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
10248
11259
 
10249
11260
  //# sourceMappingURL=index.js.map