Find a name reused across scopes
Find where repeated content crosses from one bracket depth to another, such as a name bound outside a block and used inside it, and compare terms up to the names they bind (relation).
$ cat totals.js
let total = 0;
for (const item of items) {
if (item.ok) { total = total + item.size; }
}
report(total);
Where a reuse crosses a scope
A reuse joins a later occurrence of repeated content to the earlier one. Its residual is how many brackets deeper the later occurrence is, and the holonomy sums the residuals without their sign, 0 for text whose every reuse stays at one depth:
$ trex relation totals.js --edges
trex relation: 106 bytes, 56 tokens, max depth 2, nesting load 34
... the readings and every edge ...
reuse chords (from -> to, residual = scope jump):
item -> item residual +1
total -> total residual +2
total -> total residual +0
item -> item residual +0
total -> total residual -1
total is declared at depth 0 and first used two brackets in, inside the for block and the
if block, the reuse of residual +2; the loop variable item reaches one bracket deeper. The net
holonomy keeps the signs, so +2 says the reuse flows inward on the whole: the final
report(total) comes back out by one.
Terms equal up to the names they bind
The first word inside a [ binds a name for the rest of the group. The canonical form writes
that binder as # and each later use of it as ^k, the binder k groups out, so two terms that
differ only in the names they bind read alike, and a name used in its binder’s place stands out:
$ trex relation --canonical --text 'map [item (item.size)] items'
... the readings ...
canonical form (alpha-equivalence):
map [ # ( ^0 . size ) ] items
$ trex relation --canonical --text 'map [row (row.size)] items'
... the readings ...
canonical form (alpha-equivalence):
map [ # ( ^0 . size ) ] items
$ trex relation --canonical --text 'map [row (item.size)] items'
... the readings ...
canonical form (alpha-equivalence):
map [ # ( item . size ) ] items
The third term binds row and never uses it, reading the outer item instead: its canonical form
keeps item as a name bound nowhere, where the other two read the binder.