See attached WS.
#!/usr/local/bin/apl --script
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
⍝
⍝ dbl-check-2.apl 2016-07-11 21:42:51 (GMT-7)
⍝
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
∇z←pkg⍙alp
z←'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
∇
∇z←pkg⍙dig
z←'1234567890'
∇
∇z←pkg⍙empty_string text;m
⍝ Remove content from quoted string, leaving only a pair of quotes.
⍝ This is useful to hide quoted text from analysis.
z←((¯1⌽m)∨m←~≠\text='''')/text
z←(~2↓(m,0 0)∧(0 0,m←z=''''))/z
∇
∇z←(pred pkg⍙filter1) list
⍝ Filter items of list according to unary predicate.
z←(,⊃pred¨list)/list
∇
∇z←pkg⍙function_refs func;labels;name;locals;tokens;ids
⍝ For a function expressed as a list of lines, return a list of all
⍝ nonlocal names used in the function.
z←labels←⍬
(name locals)←pkg⍙header_info pkg⍙parse 1⊃func
more:
func←1↓func
→(0=⍴func)/done
tokens←pkg⍙parse pkg⍙empty_string pkg⍙strip_comment 1⊃func
ids←pkg⍙is_id pkg⍙filter1 tokens
ids←(~ids∊locals)/ids
labels←labels,pkg⍙label tokens
z←z,ids
→more
done:
z←∪z~labels
∇
∇z←pkg⍙header_info tokens;queue;locals;t;op;name
⍝ Given a parsed function or operator header line, return the
⍝ function or operator name and a list of local variable names.
⍝
⍝ z←foo;locals
⍝ z←foo b;locals
⍝ z←a foo b;locals
⍝ z←(x foo);locals
⍝ z←(x foo) b;locals
⍝ z←(x foo y);locals
⍝ z←(x foo y) b;locals
⍝ z←a (x foo) b;locals
⍝ z←a (x foo y) b;locals
queue←locals←name←⍬
op←0
more:
→(0=⍴tokens)/end
t←1⊃tokens
tokens←1↓tokens
→('←'∊t)/assign
→('('∊t)/op_start
→((')'∊t)∧(op=1))/op_end
→(';'∊t)/locals_list
⍎(pkg⍙is_id t)/'queue←queue,⊂t'
→more
assign:
locals←locals,queue
queue←⍬
→more
op_start:
locals←locals,queue
queue←⍬
op←1
→more
op_end:
locals←locals,⊂1⊃queue
name←2⊃queue
⍎(3=⍴queue)/'locals←locals,⊂3⊃queue'
queue←⍬
→more
locals_list:
⍎(pkg⍙is_id t)/'locals←locals,⊂t'
→(0=⍴tokens)/end
t←1⊃tokens
tokens←1↓tokens
→locals_list
end:
→(0≠⍴name)/out
⍎(1=⍴queue)/'name←1⊃queue ◊ queue←⍬'
⍎(2=⍴queue)/'name←1⊃queue ◊ queue←1↓queue'
⍎(3=⍴queue)/'name←2⊃queue ◊ queue←1 0 1/queue'
out:
z←name (locals,queue)
∇
∇z←pkg⍙id1
z←'⎕∆⍙_'
∇
∇z←pkg⍙idX
z←'∆⍙_¯'
∇
∇z←pkg⍙is_id token
⍝ Return true if token is a valid identifier.
z←((1↑,token)∊pkg⍙id1,pkg⍙alp)∧(∧/(1↓,token)∊pkg⍙idX,pkg⍙alp,pkg⍙dig)
∇
∇z←pkg⍙label tokens
⍝ Given a parsed function line, return the name of a label which
⍝ appears on that line.
z←(1⌽∨/¨':'=¨tokens)/tokens
∇
∇z←pkg⍙nu1
z←'¯'
∇
∇z←pkg⍙nuX
z←'JE¯.'
∇
∇z←pkg⍙parse text;c;i;n;f
⍝ Parse text into a list of identifiers and interstital text. Note
⍝ that the interstitial text preserves blanks; this is useful when we
⍝ want to rewrite the identifiers in a text without changing the
⍝ layout.
z←c←⍬
i←n←0
more:
→(0=⍴text)/done
f←1↑text
text←1↓text
→((f∊pkg⍙nuX,pkg⍙dig)∧(n=1))/accum
→(((f∊pkg⍙idX,pkg⍙alp,pkg⍙dig)∧(i=1))∨((f∊pkg⍙nuX,pkg⍙dig)∧(n=1)))/accum
→((f∊pkg⍙id1,pkg⍙alp)∧(i=0))/start_id
→((f∊pkg⍙nu1,pkg⍙dig)∧(i=0)∧(n=0))/start_num
⍎((0≠⍴c)∧(i=1)∨(n=1))/'z←z,⊂c ◊ c←⍬'
i←n←0
→accum
start_id:
i←1
→flush
start_num:
n←1
flush:
⍎(0≠⍴c)/'z←z,⊂c ◊ c←⍬'
accum:
c←c,f
→more
done:
z←z,⊂c
∇
∇pkg⍙refresh_fn_refs;ft;all;outdated;fdef;times;fn;fns;names;i;entry;new;refs
⍝ Refresh pkg⍙⍙fn_refs_cache.
ft←0=⍴pkg⍙⍙fn_refs_cache
⍎(ft)/'⎕←⊂''Building complete function references cache...'''
all←pkg⍙⍙strip_trailing_blanks¨⊂[2]⎕nl 3 4
→(0=⍴pkg⍙⍙fn_refs_cache)/add_new
⍝ Remove deleted
pkg⍙⍙fn_refs_cache←(~(1⊃¨pkg⍙⍙fn_refs_cache)∊⊂¨all)/pkg⍙⍙fn_refs_cache
⍝ Update changed
→(0=⍴pkg⍙⍙fn_refs_cache)/add_new
outdated←⍬
names←1⊃¨pkg⍙⍙fn_refs_cache
times←2⊃¨pkg⍙⍙fn_refs_cache
fns←all
more1: →(0=⍴fns)/update
fn←↑fns
fns←1↓fns
i←names⍳⊂fn
→(i>⍴names)/more1
→(times[i]≡⊂2 ⎕at fn)/more1
outdated←outdated,⊂fn
→more1
update:
names←1⊃¨pkg⍙⍙fn_refs_cache
pkg⍙⍙fn_refs_cache←(~names∊outdated)/pkg⍙⍙fn_refs_cache
more2: →(0=⍴outdated)/add_new
fn←↑outdated
outdated←1↓outdated
fdef←⊂[2]⎕cr fn
→(0=⍴fdef)/more2
entry←⊂(fn) (2 ⎕at fn) ((⊂⍋⊃refs)⌷refs←pkg⍙function_refs fdef)
pkg⍙⍙fn_refs_cache←pkg⍙⍙fn_refs_cache,entry
→more2
⍝ Add new
add_new:
new←all
→(0=⍴pkg⍙⍙fn_refs_cache)/more3
new←(~all∊1⊃¨pkg⍙⍙fn_refs_cache)/all
more3: →(0=⍴new)/done
fn←↑new
new←1↓new
fdef←⊂[2]⎕cr fn
→(0=⍴fdef)/more3
entry←⊂(fn) (2 ⎕at fn) ((⊂⍋⊃refs)⌷refs←pkg⍙function_refs fdef)
pkg⍙⍙fn_refs_cache←pkg⍙⍙fn_refs_cache,entry
→more3
done:
⍎(ft)/'⎕←⊂''Done. Updates will be done incrementally.'' ◊ ⎕←'''''
∇
∇z←pkg⍙strip_comment text
⍝ Strip an APL comment from text.
z←(~∨\(text='⍝')>≠\text='''')/text
∇
∇z←pkg⍙⍙strip_trailing_blanks v
⍝ Strip trailing blanks from a string.
v←∊v
z←(~⌽∧\' '=⌽⊃v)/⊃v
∇
pkg⍙⍙fn_refs_cache←0 ⍝ prototype...
pkg⍙⍙fn_refs_cache←0⍴pkg⍙⍙fn_refs_cache
pkg⍙refresh_fn_refs
)check