#!/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