@suss/recognize 0.12.0 → 0.13.0

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/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/compile.ts
2
2
  import { storageBinding } from "@suss/behavioral-ir";
3
+ import { readSqlAccess, sqlFromParts } from "@suss/sql";
3
4
 
4
5
  // src/ops.ts
5
6
  function declaredBy(...importedFrom) {
@@ -14,104 +15,297 @@ function opsIn(ctx) {
14
15
  }
15
16
 
16
17
  // src/compile.ts
18
+ var MAX_RECEIVER_HOPS = 8;
17
19
  function compile(chain, recognition) {
18
20
  return (_call, ctx) => {
19
21
  const ops = opsIn(ctx);
20
- if (ops === null) {
22
+ const methods = methodsIn(chain);
23
+ if (ops === null || methods === null) {
21
24
  return null;
22
25
  }
23
- const method = ops.method();
24
- if (method === null) {
25
- return null;
26
- }
27
- const meaning = meaningOf(chain, method);
28
- if (meaning === null || !startsHere(chain, ops)) {
29
- return null;
26
+ for (const subject of subjectsOf(chain, ops)) {
27
+ const method = operationOf(subject);
28
+ const meaning = method === null ? null : listed(methods, method);
29
+ if (method === null || meaning === null || !startsHere(chain, subject)) {
30
+ continue;
31
+ }
32
+ return YIELD[chain.ending.yields]({
33
+ ops,
34
+ subject,
35
+ method,
36
+ meaning,
37
+ chain,
38
+ recognition
39
+ });
30
40
  }
31
- return YIELD[chain.ending.yields]({
32
- ops,
33
- method,
34
- meaning,
35
- chain,
36
- recognition
37
- });
41
+ return null;
38
42
  };
39
43
  }
40
- function meaningOf(chain, method) {
41
- const link = chain.links.find(
42
- (candidate) => candidate.asks === "methods"
43
- );
44
- if (link === void 0) {
45
- return null;
46
- }
44
+ function linkIn(chain, asks) {
45
+ const link = chain.links.find((candidate) => candidate.asks === asks);
46
+ return link ?? null;
47
+ }
48
+ function methodsIn(chain) {
49
+ return linkIn(chain, "methods");
50
+ }
51
+ function operationOf(ops) {
52
+ return ops.method() ?? ops.calleeText();
53
+ }
54
+ function listed(link, method) {
47
55
  if (!link.ignoringCase) {
48
56
  return link.table[method] ?? null;
49
57
  }
50
58
  return link.table[method.toLowerCase()] ?? null;
51
59
  }
60
+ function subjectsOf(chain, ops) {
61
+ const link = linkIn(chain, "subject");
62
+ return link === null ? [ops] : walk([ops], link.of);
63
+ }
64
+ var STEP = {
65
+ receiver: (step, ops) => receiversOf(ops, step.method),
66
+ argument: (step, ops) => argumentsOf(ops, step)
67
+ };
68
+ function walk(from, steps) {
69
+ let reached = [...from];
70
+ for (const step of steps) {
71
+ reached = reached.flatMap((ops) => STEP[step.to](step, ops));
72
+ }
73
+ return reached;
74
+ }
75
+ function receiversOf(ops, method) {
76
+ let step = ops.receiver();
77
+ for (let hops = 0; step !== null && hops < MAX_RECEIVER_HOPS; hops += 1) {
78
+ if (method === void 0 || step.method() === method) {
79
+ return [step];
80
+ }
81
+ step = step.receiver();
82
+ }
83
+ return [];
84
+ }
85
+ function argumentsOf(ops, step) {
86
+ const found = [];
87
+ for (const index of positions(ops, step.at)) {
88
+ const argument = ops.argument(index);
89
+ if (argument !== null && cameFrom(argument, step.origin)) {
90
+ found.push(argument);
91
+ }
92
+ }
93
+ return found;
94
+ }
95
+ function cameFrom(argument, origin) {
96
+ return origin === void 0 || argument.isFrom(origin);
97
+ }
98
+ function positions(ops, pick) {
99
+ if (typeof pick === "number") {
100
+ return [pick];
101
+ }
102
+ const first = "at" in pick ? pick.at : pick.from;
103
+ const last = "at" in pick ? pick.at + 1 : ops.argumentCount();
104
+ const found = [];
105
+ for (let index = first; index < last; index += 1) {
106
+ found.push(index);
107
+ }
108
+ return found;
109
+ }
52
110
  var START = {
53
111
  receiver: (start, ops) => ops.receiverIsFrom(start.origin)
54
112
  };
55
- function startsHere(chain, ops) {
113
+ function startsHere(chain, subject) {
56
114
  return chain.links.every(
57
- (link) => link.asks === "start" ? START[link.at.starts](link.at, ops) : true
115
+ (link) => link.asks === "start" ? START[link.at.starts](link.at, subject) : true
58
116
  );
59
117
  }
60
118
  var YIELD = {
61
- storageAccess
119
+ storageAccess,
120
+ sqlAccess
62
121
  };
63
122
  function storageAccess(matched) {
64
- const { ops, method, meaning, chain, recognition } = matched;
123
+ const { subject, chain } = matched;
124
+ const meaning = matched.meaning;
125
+ const unsettled = chain.ending.unsettledName;
126
+ const kind = kindOf(meaning.kind, subject, unsettled);
127
+ const link = linkIn(chain, "input");
128
+ const input = link === null ? null : statedValue(subject, link.at);
129
+ if (kind === null || link !== null && input === null) {
130
+ return null;
131
+ }
132
+ return reachedBy(chain, subject, unsettled).map(
133
+ (reached) => accessEffect(matched, { input, kind, reached, unsettled })
134
+ );
135
+ }
136
+ function accessEffect(matched, access) {
137
+ const { ops, subject, method, chain, recognition } = matched;
138
+ const meaning = matched.meaning;
65
139
  const ending = chain.ending;
66
- const selector = namesAt(ops, meaning.selector, ending.unsettledName);
67
- const fields = namesAt(ops, meaning.fields, ending.unsettledName);
68
- const container = containerOf(chain, selector, ops);
69
- return [
70
- {
71
- type: "interaction",
72
- binding: storageBinding({
73
- recognition,
74
- storageSystem: ending.system,
75
- ...ending.transport === void 0 ? {} : { transport: ending.transport },
76
- scope: ending.scope,
77
- container,
78
- accessPath: null
79
- }),
80
- callee: ops.calleeText(),
81
- interaction: {
82
- class: "storage-access",
83
- kind: meaning.kind,
84
- fields,
85
- operation: method,
86
- ...selector.length > 0 ? { selector } : {}
87
- }
140
+ const { input, kind, reached, unsettled } = access;
141
+ const stated = { input, entry: reached.entry, kind };
142
+ const selector = namesFor(meaning.selector, subject, stated, unsettled);
143
+ const fields = namesFor(meaning.fields, subject, stated, unsettled);
144
+ return {
145
+ type: "interaction",
146
+ binding: storageBinding({
147
+ recognition,
148
+ storageSystem: ending.system,
149
+ ...ending.transport === void 0 ? {} : { transport: ending.transport },
150
+ scope: ending.scope,
151
+ container: reached.entry === null ? containerOf(chain, selector, subject, unsettled) : reached.container,
152
+ accessPath: accessPathOf(chain, subject, unsettled)
153
+ }),
154
+ callee: ops.calleeText(),
155
+ interaction: {
156
+ class: "storage-access",
157
+ kind,
158
+ fields,
159
+ operation: namesAt(subject, meaning.operation, unsettled)[0] ?? method,
160
+ ...selector.length > 0 ? { selector } : {}
88
161
  }
89
- ];
162
+ };
90
163
  }
91
- function containerOf(chain, selector, ops) {
92
- const link = chain.links.find(
93
- (candidate) => candidate.asks === "container"
94
- );
95
- if (link === void 0) {
164
+ function reachedBy(chain, subject, unsettled) {
165
+ const link = linkIn(chain, "containers");
166
+ const map = link === null ? null : statedValue(subject, link.in);
167
+ if (map === null) {
168
+ return [{ container: null, entry: null }];
169
+ }
170
+ return map.entries(unsettled).map((entry) => ({ container: entry.key, entry: entry.value }));
171
+ }
172
+ function kindOf(kind, subject, unsettled) {
173
+ if (typeof kind === "string") {
174
+ return kind;
175
+ }
176
+ const asked = kind;
177
+ const [answer] = namesAt(subject, asked.asks, unsettled);
178
+ const said = answer === void 0 ? void 0 : asked.means[answer];
179
+ return said ?? asked.otherwise ?? null;
180
+ }
181
+ function containerOf(chain, selector, subject, unsettled) {
182
+ const link = linkIn(chain, "container");
183
+ if (link === null) {
96
184
  return selector[0] ?? null;
97
185
  }
98
- return link.from(selector, ops);
186
+ if ("from" in link) {
187
+ return link.from(selector, subject);
188
+ }
189
+ return namesAt(subject, link.argument, unsettled)[0] ?? null;
190
+ }
191
+ function accessPathOf(chain, subject, unsettled) {
192
+ const link = linkIn(chain, "accessPath");
193
+ return link === null ? null : namesAt(subject, link.argument, unsettled)[0] ?? null;
194
+ }
195
+ function statedValue(subject, pick) {
196
+ for (const ops of walk([subject], pick.of ?? [])) {
197
+ const stated = ops.valueAt(pick.at);
198
+ if (stated === null) {
199
+ continue;
200
+ }
201
+ if (pick.property === void 0) {
202
+ return stated;
203
+ }
204
+ for (const property of pick.property) {
205
+ const inside = stated.property(property);
206
+ if (inside !== null) {
207
+ return inside;
208
+ }
209
+ }
210
+ }
211
+ return null;
99
212
  }
100
- function namesAt(ops, pick, unsettled) {
213
+ var NOTHING_STATED = {
214
+ text: () => null,
215
+ flag: () => null,
216
+ entries: () => [],
217
+ items: () => [],
218
+ property: () => null,
219
+ parts: () => null,
220
+ holes: () => []
221
+ };
222
+ function namesFor(says, subject, stated, unsettled) {
223
+ if (says === void 0) {
224
+ return [];
225
+ }
226
+ if (Array.isArray(says)) {
227
+ return [...says];
228
+ }
229
+ if (typeof says === "function") {
230
+ const input2 = stated.input;
231
+ return input2 === null ? [] : [...says({ ...stated, input: input2 })];
232
+ }
233
+ const pointed = says;
234
+ if (!("by" in pointed)) {
235
+ return namesAt(subject, pointed, unsettled);
236
+ }
237
+ const input = statedValue(subject, pointed.of) ?? NOTHING_STATED;
238
+ return [...pointed.by({ ...stated, input })];
239
+ }
240
+ function namesAt(subject, pick, unsettled) {
101
241
  if (pick === void 0) {
102
242
  return [];
103
243
  }
104
- const last = "at" in pick ? pick.at + 1 : ops.argumentCount();
105
- const first = "at" in pick ? pick.at : pick.from;
106
244
  const names = [];
107
- for (let index = first; index < last; index += 1) {
108
- const name = ops.nameAt(index, unsettled);
109
- if (name !== null) {
110
- names.push(name);
245
+ for (const ops of walk([subject], pick.of ?? [])) {
246
+ for (const index of positions(ops, pick)) {
247
+ const name = nameOf(ops, pick, index, unsettled);
248
+ if (name !== null) {
249
+ names.push(name);
250
+ }
111
251
  }
112
252
  }
113
253
  return names;
114
254
  }
255
+ function nameOf(ops, pick, index, unsettled) {
256
+ if (pick.property === void 0) {
257
+ return ops.nameAt(index, unsettled);
258
+ }
259
+ for (const property of pick.property) {
260
+ const name = ops.propertyAt(index, property, unsettled);
261
+ if (name !== null) {
262
+ return name;
263
+ }
264
+ }
265
+ return null;
266
+ }
267
+ function sqlAccess(matched) {
268
+ const { ops, subject, method, chain, recognition } = matched;
269
+ const ending = chain.ending;
270
+ const stated = statedValue(subject, matched.meaning.statement);
271
+ const parts = stated?.parts() ?? null;
272
+ if (stated === null || parts === null) {
273
+ return null;
274
+ }
275
+ const statement = sqlFromParts(parts, namesInHoles(chain, stated));
276
+ const accesses = readSqlAccess(statement, { dialect: ending.dialect });
277
+ return accesses.length === 0 ? null : accesses.map((access) => ({
278
+ type: "interaction",
279
+ binding: storageBinding({
280
+ recognition,
281
+ storageSystem: ending.system,
282
+ ...ending.transport === void 0 ? {} : { transport: ending.transport },
283
+ scope: ending.scope,
284
+ container: access.table
285
+ }),
286
+ callee: ops.calleeText(),
287
+ interaction: {
288
+ class: "storage-access",
289
+ kind: access.kind,
290
+ fields: access.fields,
291
+ ...access.selector.length > 0 ? { selector: access.selector } : {},
292
+ operation: method
293
+ }
294
+ }));
295
+ }
296
+ function namesInHoles(chain, stated) {
297
+ const link = linkIn(chain, "interpolates");
298
+ if (link === null) {
299
+ return [];
300
+ }
301
+ return stated.holes().map((hole) => nameInHole(hole, link));
302
+ }
303
+ function nameInHole(hole, link) {
304
+ if (hole === null || link.from !== void 0 && !hole.isFrom(link.from)) {
305
+ return null;
306
+ }
307
+ return namesAt(hole, link.named, "nothing")[0] ?? null;
308
+ }
115
309
 
116
310
  // src/example.ts
117
311
  function runExamples(pack2, run) {
@@ -134,24 +328,35 @@ function examplesMissing(pack2) {
134
328
  }
135
329
 
136
330
  // src/pack.ts
331
+ var DISPATCHED_ON = {
332
+ storageAccess: "invocation",
333
+ sqlAccess: "access"
334
+ };
335
+ function walkedBy(chains, walk2, recognizedAs) {
336
+ return chains.filter((chain) => DISPATCHED_ON[chain.ending.yields] === walk2).map((chain) => compile(chain, recognizedAs));
337
+ }
137
338
  function pack(name, matches, spec) {
138
339
  const chains = matches.map((match) => match.declared);
139
- const recognizers = chains.map(
140
- (chain) => compile(chain, spec.recognizedAs)
141
- );
340
+ const access = walkedBy(chains, "access", spec.recognizedAs);
142
341
  return {
143
342
  name,
144
343
  languages: spec.languages,
145
344
  ...spec.version === void 0 ? {} : { version: spec.version },
146
- protocol: protocolOf(chains),
345
+ protocol: spec.protocol ?? protocolOf(chains),
147
346
  discovery: [],
148
347
  terminals: [],
149
348
  inputMapping: { type: "positionalParams", params: [] },
150
- requiresImport: gateOf(chains),
151
- invocationRecognizers: recognizers,
152
- declarations: { declarations: chains.map(describe) }
349
+ requiresImport: [
350
+ .../* @__PURE__ */ new Set([...gateOf(chains), ...spec.requiresImport ?? []])
351
+ ],
352
+ invocationRecognizers: walkedBy(chains, "invocation", spec.recognizedAs),
353
+ ...access.length === 0 ? {} : { accessRecognizers: access },
354
+ declarations: declarationsIn(matches)
153
355
  };
154
356
  }
357
+ function declarationsIn(matches) {
358
+ return { declarations: matches.map((match) => describe(match.declared)) };
359
+ }
155
360
  function protocolOf(chains) {
156
361
  const wires = new Set(
157
362
  chains.map((chain) => chain.ending.transport ?? chain.ending.system)
@@ -167,42 +372,97 @@ function protocolOf(chains) {
167
372
  function gateOf(chains) {
168
373
  const modules = /* @__PURE__ */ new Set();
169
374
  for (const chain of chains) {
170
- for (const link of chain.links) {
171
- if (link.asks !== "start" || link.at.starts !== "receiver") {
172
- continue;
173
- }
174
- for (const module of link.at.origin.importedFrom) {
375
+ for (const origin of originsIn(chain)) {
376
+ for (const module of origin.importedFrom) {
175
377
  modules.add(module);
176
378
  }
177
379
  }
178
380
  }
179
381
  return [...modules];
180
382
  }
383
+ function originsIn(chain) {
384
+ const found = [];
385
+ for (const link of chain.links) {
386
+ if (link.asks === "start" && link.at.starts === "receiver") {
387
+ found.push(link.at.origin);
388
+ }
389
+ if (link.asks === "interpolates" && link.from !== void 0) {
390
+ found.push(link.from);
391
+ }
392
+ if (link.asks !== "subject") {
393
+ continue;
394
+ }
395
+ for (const step of link.of) {
396
+ if (step.to === "argument" && step.origin !== void 0) {
397
+ found.push(step.origin);
398
+ }
399
+ }
400
+ }
401
+ return found;
402
+ }
181
403
  function describe(chain) {
182
- const functions = chain.links.filter(isFunctionLink);
404
+ const links = chain.links.filter(isFunctionLink);
405
+ const written = [
406
+ ...links.map((link) => ({
407
+ asks: link.asks,
408
+ reachesAst: link.from.reachesAst === true
409
+ })),
410
+ ...rulesIn(chain)
411
+ ];
183
412
  return {
184
413
  name: chain.ending.system,
185
- dataLinks: chain.links.length - functions.length,
186
- functionLinks: functions.map((link) => link.asks),
187
- astLinks: functions.filter((link) => link.from.reachesAst === true).map((link) => link.asks),
414
+ // A method table with a rule inside it is still a table, so that
415
+ // link stays counted as data and the rule is priced beside it.
416
+ dataLinks: chain.links.length - links.length,
417
+ functionLinks: written.map((rule) => rule.asks),
418
+ astLinks: written.filter((rule) => rule.reachesAst).map((rule) => rule.asks),
188
419
  example: chain.example
189
420
  };
190
421
  }
191
422
  function isFunctionLink(link) {
192
- return link.asks === "container";
423
+ return link.asks === "container" && "from" in link;
424
+ }
425
+ function rulesIn(chain) {
426
+ const link = chain.links.find(
427
+ (candidate) => candidate.asks === "methods"
428
+ );
429
+ const written = /* @__PURE__ */ new Map();
430
+ for (const method of Object.values(link?.table ?? {})) {
431
+ for (const asks of ["selector", "fields"]) {
432
+ const rule = ruleFor(method[asks]);
433
+ if (rule !== null) {
434
+ written.set(asks, rule);
435
+ }
436
+ }
437
+ }
438
+ return [...written].map(([asks, from]) => ({
439
+ asks,
440
+ reachesAst: from.reachesAst === true
441
+ }));
442
+ }
443
+ function ruleFor(says) {
444
+ if (typeof says === "function") {
445
+ return says;
446
+ }
447
+ if (says === void 0 || Array.isArray(says)) {
448
+ return null;
449
+ }
450
+ const pointed = says;
451
+ return "by" in pointed ? pointed.by : null;
193
452
  }
194
453
 
195
- // src/storage.ts
196
- function storageCalls(spec) {
454
+ // src/sqlStatements.ts
455
+ function sqlStatements(spec) {
197
456
  const ending = {
198
- yields: "storageAccess",
457
+ yields: "sqlAccess",
199
458
  system: spec.system,
200
459
  ...spec.transport === void 0 ? {} : { transport: spec.transport },
201
460
  scope: spec.scope ?? "default",
202
- unsettledName: spec.unsettledName ?? "reference"
461
+ dialect: spec.dialect
203
462
  };
463
+ const client = spec.client;
204
464
  return chainFrom({
205
- links: [{ asks: "start", at: { starts: "receiver", origin: spec.client } }],
465
+ links: client === void 0 ? [] : [{ asks: "start", at: { starts: "receiver", origin: client } }],
206
466
  ending,
207
467
  example: null
208
468
  });
@@ -216,17 +476,55 @@ function chainFrom(declared) {
216
476
  table,
217
477
  ignoringCase: options?.ignoringCase ?? false
218
478
  }),
219
- container: (from) => adding({ asks: "container", from }),
479
+ interpolating: (says) => adding({ asks: "interpolates", ...says }),
220
480
  example: (code) => chainFrom({ ...declared, example: code })
221
481
  };
222
482
  }
483
+
484
+ // src/storage.ts
485
+ function storageCalls(spec) {
486
+ const ending = {
487
+ yields: "storageAccess",
488
+ system: spec.system,
489
+ ...spec.transport === void 0 ? {} : { transport: spec.transport },
490
+ scope: spec.scope ?? "default",
491
+ unsettledName: spec.unsettledName ?? "reference"
492
+ };
493
+ const client = spec.client;
494
+ return chainFrom2({
495
+ links: client === void 0 ? [] : [{ asks: "start", at: { starts: "receiver", origin: client } }],
496
+ ending,
497
+ example: null
498
+ });
499
+ }
500
+ function chainFrom2(declared) {
501
+ const adding = (link) => chainFrom2({ ...declared, links: [...declared.links, link] });
502
+ return {
503
+ declared,
504
+ about: (...of) => adding({ asks: "subject", of }),
505
+ methods: (table, options) => adding({
506
+ asks: "methods",
507
+ table,
508
+ ignoringCase: options?.ignoringCase ?? false
509
+ }),
510
+ container: (says) => adding(
511
+ typeof says === "function" ? { asks: "container", from: says } : { asks: "container", argument: says }
512
+ ),
513
+ accessPath: (says) => adding({ asks: "accessPath", argument: says }),
514
+ containersIn: (says) => adding({ asks: "containers", in: says }),
515
+ input: (says) => adding({ asks: "input", at: says }),
516
+ example: (code) => chainFrom2({ ...declared, example: code })
517
+ };
518
+ }
223
519
  export {
224
520
  compile,
225
521
  constructedFrom,
522
+ declarationsIn,
226
523
  declaredBy,
227
524
  examplesMissing,
228
525
  opsIn,
229
526
  pack,
230
527
  runExamples,
528
+ sqlStatements,
231
529
  storageCalls
232
530
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/recognize",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "Write a pack as data: a chain of named links that any language adapter with the executor ops can run.",
6
6
  "keywords": [
@@ -49,8 +49,9 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@suss/behavioral-ir": "0.12.0",
53
- "@suss/extractor": "0.12.0"
52
+ "@suss/behavioral-ir": "0.13.0",
53
+ "@suss/extractor": "0.13.0",
54
+ "@suss/sql": "0.13.0"
54
55
  },
55
56
  "devDependencies": {
56
57
  "tsup": "*",