APL/D Source Code:

────────────────────────────────────────────────────────────────────────────────

    eval←exp.evalexp          ⍝ eval is function evalexp in namespace exp.

────────────────────────────────────────────────────────────────────────────────

evalexp←{⎕ML←0                      ⍝ Evaluate expression ⍵ with defs, fmt: ⍺.
    defs←0⍴⊂'' 0                    ⍝ null definitions vector.
    ⍺←defs 0                        ⍝ default: no defs, standard format.
    0=≡⍺:defs ⍺ ∇ ⍵                 ⍝ format only: default null defs.
    3=|≡⍺:⍺ 0 ∇ ⍵                   ⍝ defs only: default standard format.
    0 3≡|≡¨⍺:(⌽⍺)∇ ⍵                ⍝ fmt defs: reverse.
    usage←'[defs] [fmt] eval expr'  ⍝ usage message.
    emsg←'usage: ',usage            ⍝ error message.
    3 0≢|≡¨⍺:emsg ⎕SIGNAL 11        ⍝ bad left arg: signal domain.
    defs fmt←⍺                      ⍝ definitions and format.
    666::(↑⍬⍴⎕DM)⎕SIGNAL ⎕EN        ⍝ pass back error.
    tree←parse ⍵                    ⍝ parse to expression tree.
    expr←defs evaluate tree         ⍝ evaluate simple tree nodes.
    rexp←reduce expr                ⍝ reduce resulting expression tree.
    fmt show rexp                   ⍝ show numeric or character result.
}

────────────────────────────────────────────────────────────────────────────────

until←{                                 ⍝ Loop until input matches ⍵ terminator.
    defs←0⍴⊂'' 0                        ⍝ null definitions vector.
    ⍺←defs 0                            ⍝ default: no defs, standard format.
    0=≡⍺:defs ⍺ ∇ ⍵                     ⍝ format only: default null defs.
    3=|≡⍺:⍺ 0 ∇ ⍵                       ⍝ defs only: default standard format.
    0 3≡|≡¨⍺:(⌽⍺)∇ ⍵                    ⍝ fmt defs: reverse.
    usage←'[defs] [fmt] until stop'     ⍝ usage message.
    emsg←'usage: ',usage                ⍝ error message.
    3 0≢|≡¨⍺:emsg ⎕SIGNAL 11            ⍝ bad left arg: signal domain.
    ⍺ exp.{                             ⍝ with exp:
        defn←{∧/(1=≡∘⊃¨⍵),,↑2=⍴¨⍵}      ⍝ definition: x=...
        defs fmt←⍺                      ⍝ split defs from format.
        defs{                           ⍝ loop:
            nxt←⍞⊣⍞←4↑''                ⍝ next (commented) input line.
            src←(∧\nxt≠'⍝')/nxt         ⍝ raw source.
            ⍵≡src~' ':defs←⍺            ⍝ terminator: return (shy) definitions.
            0=⍴src~' ':⍺ ∇ ⍵            ⍝ blank line: ignore.
            666::⍺ ∇ ⍵⊣⎕←↑⍬⍴⎕DM         ⍝ error: display and continue.
            tree←parse src              ⍝ parsed tree from source.
            expr←⍺ evaluate tree        ⍝ evaluated parse tree,
            defn expr:expr ∇ ⍵          ⍝ defn: continue with extended envt.
            rexp←reduce expr            ⍝ reduced expression.
            ⎕←fmt show rexp             ⍝ show formatted result,
            ⍺ ∇ ⍵                       ⍝ and try again,
        }⍵                              ⍝ until stop.
    }⍵~' '                              ⍝ :endwith exp.
}

────────────────────────────────────────────────────────────────────────────────

load←{⎕IO ⎕ML←0                             ⍝ Install defns and rules for src ⍵.

    prep←{                                  ⍝ prepare char vectors source.
        rmcm←{(∧\⍵≠'⍝')/⍵}                  ⍝ remove comment.
        rmwt←{(~⍵∧.=¨' ')/⍵}                ⍝ remove white lines.
        sepr←{('⋄'=⍵)∧0=-⌿+\'()'∘.=⍵}       ⍝ outer-level diamond.
        diam←{1↓¨(sepr ⍵)⊂⍵}∘{'⋄',⍵}¨       ⍝ split diamondised lines.
        ↑,/diam rmwt rmcm¨↓⎕FMT↑⍵           ⍝ clean up source vector.
    }

    comp←{                                  ⍝ compile function definitions.
        sepr←{↑¨,/¨↓⍉↑(⍳⍴⍵)join¨⍵}          ⍝ separate.
        join←{↓⍉↑⍺,¨↑,¨/⍵}                  ⍝ join fields.
        tupl←{{1↓¯1↓⍵}\(1,1↓<\⍵='(')⊂⍵}     ⍝ asc-apl pair.
        macro←{                             ⍝ convert macro body to dfn.
            name body←⍵                     ⍝ macro name and body.
            ~'→'∊body:⍵                     ⍝ raw apl function: done.
            masks←0 1=⊂∨\body='→'           ⍝ masks to split args from code.
            args code←0 1↓¨masks/¨⊂body     ⍝ macro args and code.
            sig←'0::⎕signal ⎕en ⋄ '         ⍝ catchall to pass back error.
            tag←args,' _←⍵,0 ⋄ '            ⍝ argument vector item naming.
            dfn←'{',sig,tag,code,'}'        ⍝ extended dfn.
            name dfn                        ⍝ macro assoc and defn.
        }
        split←{(1,1↓(top ⍵)∧⍵∊' ')⊂⍵}       ⍝ split between definition segments.
        pair←{                              ⍝ assocs, then token vectors.
            ascs←('←→'⍳⊃⍵)⊃'←→↑'            ⍝ association character.
            defs←split(ascs∊'←→')↓⍵         ⍝ following function definitions,
            ascs(defs~⊂,' ')                ⍝ association code followed by defs.
        }                                   ⍝ for each function definition line.
        segs←rlbs¨\¨⌽pair¨⍵                 ⍝ defn segments strongest first.
        sepr macro¨\¨tupl¨\¨segs            ⍝ (strength assoc asc apl)
    }

    rlbs←{(∨\⍵≠' ')/⍵}                      ⍝ remove leading blanks.
    top←{{⍵∨¯1⌽⍵}0=-⌿+\'()'∘.=⍵}            ⍝ top level chars (not in parens).

    infix←{(⊃⍵)∊'←→'}                       ⍝ infix function line?
    prefix←{~1∊'=≡'∊(top ⍵)/⍵}              ⍝ monadic function line?
    fndef←{(prefix ⍵)∨infix ⍵}              ⍝ function definition line?
    type←{2⌊(fndef ⍵)+2×1∊'::'⍷⍵}           ⍝ 0: redn, 1:fndef, 2: patn var.

    lines←rlbs¨prep ⍵                       ⍝ left aligned definition lines.
    masks←0 1 2=⊂type¨lines                 ⍝ type of each line.
    rsrc fsrc psrc←masks/¨⊂lines            ⍝ separate source lines.

    exp.defns←comp fsrc                     ⍝ function definition library.
    exp.(pvars pchks)←aux.pcomp psrc        ⍝ pattern vars and predicates.
    exp.rules←1↓{                           ⍝ reduction rules library.
        666::(⍵,(⊃⌽⎕TC),⊃⎕DM)⎕SIGNAL 666    ⍝ bad line: display and exit.
        exp.(strip parse ⍵)                 ⍝ parse script source line.
    }¨'0',rsrc                              ⍝ for each source line.

    1:rslt←exp.(defns rules pvars pchks)    ⍝ shy rslt.
}

────────────────────────────────────────────────────────────────────────────────

parse←{⎕IO ⎕ML←0                                ⍝ Infix expression parser.

    lex←{                                       ⍝ tokens vector from source.
        cons←{⍵[⍒↑⍴¨⍵]}⍺,lp rp sp               ⍝ constants, longest first.
        hits←cons⍷¨⊂⍵                           ⍝ masks of constant positions.
        mask←<⍀↑cons{↑∨/(-⍳⍴⍺)⌽¨⊂⍵}¨hits        ⍝ matrix mask of all constants.
        part←{⍺{⍵∨(-⍺)⌽⍵}(⍺⍴1)⍷⍵}               ⍝ partition vector from mask.
        toks←(1,1↓↑∨⌿(⍴¨cons)part¨↓mask)⊂⍵      ⍝ token vector.
        pvec←¯1++\⊃∘⍴¨toks                      ⍝ vector of token positions.
        (,∘1~toks∊⊂,' ')/↓⍉↑(toks,o)(pvec,⍴⍵)   ⍝ (token position) pairs.
    }

    tree←{                                      ⍝ generate parse tree.
        cls dep acc stk←⍺                       ⍝ parse state.
        (tok pos)toks←⍵                         ⍝ next token and remainder.
        tok≡o:(⍺ pos flush dep)toks             ⍝ no more tokens: finished.
        tok≡rp:((⍺ pos flush 0⌊dep-1)o)toks     ⍝ ')': flush remaining fns.
        chk←{↑{⍺ ⍵}/(2×cls=a)1/(i pos)⍵}        ⍝ inject error if case 2(3).
        tok≡lp:⍺ ∇ i(dep+1)o o ∇ chk toks       ⍝ '(': sub-expr and remainder.

        (⊂tok)∊fns:tok ∇{                       ⍝ function: bind or stack.
            bs ad←(cls=a)props ⍺ pos            ⍝ bind strength and assoc dirn.
            ∆stk←bs ad(⍺ pos)stk                ⍝ push function on stack.
            ∆acc←(cls∊f i)⊃acc(o acc)           ⍝ monadic fn: push dummy argt.
            (f dep,∆acc popfns ∆stk)⍺⍺ ⍵        ⍝ pop and bind stronger fns.
        }toks

        cls=a:pos error'missing operator'       ⍝ adjacent operands: error.
        a dep(tok acc)stk ∇ toks                ⍝ argument: accumulate.
    }

    popfns←{                                    ⍝ pop 'n bind stronger fns.
        b1 a1 f1(b2 a2 f2 stk)←⍵                ⍝ pop top two functions,
        b1 a1{                                  ⍝ top fn stronger?
            ⍵≡o o:1                             ⍝ lower fn null: yes.
            ⍺≡⍵:(⊃⌽⍺)∊'→↑'                      ⍝ same: depends on assoc.
            >/⊃¨⍺ ⍵                             ⍝ compare binding strengths.
        }b2 a2:⍺ ⍵                              ⍝ top stronger: continue.
        rgt(lft acc)←⍺                          ⍝ left and right args from acc.
        (lft f2 rgt)acc ∇ b1 a1 f1 stk          ⍝ bind top fn and try again.
    }

    props←{                                     ⍝ function properties.
        tok pos←⍵                               ⍝ token and position.
        mask←ascs∊⍺⊃'↑' '←→'                    ⍝ valency mask.
        fs bs as←mask∘/¨fns strs ascs           ⍝ fns, bind-strengths, assocs.
        (⊂tok)∊fs:(fs⍳⊂tok)⊃¨bs as              ⍝ bind-strength and assoc dirn.
        pos error'missing operand'              ⍝ bad valency: error.
    }

    flush←{                                     ⍝ no more tokens: finished.
        (cls dep acc stk)pos←⍺                  ⍝ parse state.
        cls=f:pos error'missing operand'        ⍝ final argument missing: error.
        cls=i:pos error'null expression'        ⍝ null expression (): error.
        0=⍵:⊃⊃acc popfns ¯1 o o stk             ⍝ ok: flush stack with weak fn.
        emsg←(1+×⍵)⊃'unexpected' '' 'missing'   ⍝ appropriate message.
        pos error emsg,' ',(|⍵)/')'             ⍝ mis-matched parens.
    }

    error←{⎕SIGNAL∘666,∘⍵,⍺ 1 1/' \ '}          ⍝ error function.

    strs ascs fns _←defns                       ⍝ strengths, assocs, functions.
    i a f o lp rp sp←'·⍵∇∘',,¨'() '             ⍝ parse state constants.

    ⊃i 0 o o tree↑{⍺ ⍵}/(fns lex,⍵),o           ⍝ parse token stack.
}

────────────────────────────────────────────────────────────────────────────────

evaluate←{⎕ML←0                             ⍝ Evaluate expr ⍵ in envt ⍺.

    trav←{                                  ⍝ traverse tree evaluating:
        env top←⍺                           ⍝ environment and top level ','.
        '∘'≡⍵:⍵                             ⍝ dummy left argt.
        0=≡⍵:⍵                              ⍝ number.
        1=≡⍵:env{                           ⍝ atom:
            numc ⍵:⍎⍵                       ⍝ number: evaluate.
            tags vals←↓⍉↑⌽⍺                 ⍝ name: split environment.
            (tags⍳⊂⍵)⊃vals,⊂⍵               ⍝ look up name in environment.
        }⍵

        lft(fn fp)rgt←⍵                     ⍝ split expression.

        fn≡,',':⍺ ∇{                        ⍝ "where" or arg list.
            ~top:⍺ ⍺⍺ vector ⍵              ⍝ string out vector argument.
            (⍺ ⍺⍺ rgt)top ⍺⍺ redexp lft     ⍝ top level: eval larg in envt rarg.
        }⍵

        fn≡,'=':env,⊂lft(⍺ ∇ rgt)           ⍝ defn: return extended envt.

        larg rarg←(env 0)∘∇¨lft rgt         ⍝ evaluate left and right args.
        newx←larg(fn fp)rarg                ⍝ partially evaluated expr.
        0∊numb¨larg rarg~'∘':newx           ⍝ non-number: can't evaluate.

        aplsrc←(asc⍳⊂,fn)⊃apl               ⍝ apl equivalent function.
        aplsrc≡'':newx                      ⍝ no apl equivalent: can't evaluate.
        0::fp error'bad operator'           ⍝ value error: bad function.
        aplfn←⍎aplsrc                       ⍝ apl function.

        0::fp error'bad operand'            ⍝ catchall: bad operand.
        5::fp error'wrong number of args'   ⍝ length error from lib fn.
        larg≡'∘':aplfn rarg                 ⍝ execute monadic expression.
        larg aplfn rarg                     ⍝ execute dyadic expression,
    }

    redexp←{                                ⍝ reduce exp to left of ','.
        lft(fn fp)rgt←⍵                     ⍝ extract fn.
        (⊂fn)∊,¨',=':⍵                      ⍝ another "where" to left: ignore.
        reduce ⍵                            ⍝ otherwise: reduce principal exp.
    }

    vector←{                                ⍝ vector of args from (,··) list.
        1=≡,⍵:⍺ ⍺⍺ ⍵                        ⍝ atom: done.
        larg(fn fp)rarg←⍵                   ⍝ split expression.
        ~fn≡,',':fp error'syntax error'     ⍝ e.g.: foo(x, x=2)
        ↑,/⍺∘⍺⍺¨larg rarg                   ⍝ join evaluated arguments.
    }

    666::(⊃⎕DM)⎕SIGNAL ⎕EN                  ⍝ pass back error.
    error←{(⍺ emsg ⍵)⎕SIGNAL 666}           ⍝ error function.
    emsg←{(pmsg ⍺),⍵}                       ⍝ positioned error message.
    pmsg←{(⍵≥0)/⍵ 1 1/' \ '}                ⍝ positioned error marker.

    numc←{(,1)≡⊃⎕VFI ⍵}                     ⍝ is character rep of number?
    numb←{∧/0≡¨⊃0⍴⊂⍵}                       ⍝ numeric scalar or vector?

    _ _ asc apl←defns                       ⍝ ascii and apl functions.

    ⍺←''                                    ⍝ default null environment, top.
    ⍺ 1 trav ⍵                              ⍝ evaluated expression and flib
}

────────────────────────────────────────────────────────────────────────────────

reduce←{⎕IO ⎕ML←0                       ⍝ Reduce expression tree.

    trav←{                              ⍝ traverse expr, applying rules.
        1=≡,⍵:⍵                         ⍝ atom: done.
        2=⍴⍵:⍵                          ⍝ fun pos:done.
        expr←⍺∘∇¨⍵                      ⍝ simplify sub-expressions.
        hits←⍺ expr dups ⍺ find expr    ⍝ rules that apply at this level.
        ~1∊hits:''evaluate expr         ⍝ none: continue.
        fm tc to←(hits⍳1)⊃rules         ⍝ select applicable rule.
        simp←fm to repl expr            ⍝ simplified expression.
        tc='=':⍺ ∇''evaluate simp       ⍝ try again with simpler expr.
        join←{↑⍺{⍺,⍺⍺,⍵}/⍵}             ⍝ ⍺-join of segments.
        rfmt←' → 'join show¨fm to       ⍝ formatted rule.
        prev next←show¨expr simp        ⍝ previous and next expression.
        segs←prev(rfmt join'[]')next    ⍝ trace segments.
        tabs←(0=⎕SI⍳⎕SI)/'·'            ⍝ trace ident.
        ⎕←tabs,' 'join segs             ⍝ traced reduction.
        ⍺ ∇''evaluate simp              ⍝ try again.
    }                                   ⍝ expr ← patn :: expr

    find←{                              ⍝ mask of pattern matches.
        vars←⍺ match ⍵                  ⍝ pattern variable matches.
        1=≡,⍵:vars∨⍺∊⊂⍕⍵                ⍝ leaf: match
        larg(fn fp)rarg←⍵               ⍝ expr: split.
        pfns←⊃⌽2↑↓⍉↑⍺                   ⍝ pattern functions.
        mask←(pfns∊⊂fn)∨pfns match fn   ⍝ possible matching patterns.
        ~1∊mask:vars                    ⍝ no hits: xvars match expressions.
        subs←↓⍉↑mask/⍺                  ⍝ matching sub-patterns.
        hits←↑∧/subs ∇¨larg fn rarg     ⍝ subexpression matches.
        vars∨mask\hits                  ⍝ matches including expr vars.
    }                                   ⍝ hits ← patns :: expr

    match←{                             ⍝ pattern variable matches.
        amsk←(⍺∊pvars)∧~⍵≡'∘'           ⍝ pattern variables.
        ~1∊amsk:amsk                    ⍝ none: done.
        vars←pvars∩amsk/⍺               ⍝ unique pattern vars.
        vals←⍎¨(pvars⍳vars)⊃¨⊂pchks     ⍝ corresp. check results.
        amsk\(vars⍳amsk/⍺)⊃¨⊂vals       ⍝ check result per pattern var.
    }                                   ⍝ hits ← patns :: expr

    dups←{                              ⍝ check pattern with duplicate vars.
        ~1∊⍵:⍵                          ⍝ no hits: finished.
        pats expr←⍺                     ⍝ patterns and subject expression.
        ⍵\{                             ⍝ check each hit:
            fms tos←↓⍉↑⍵ dict expr      ⍝ vars and vals.
            masks←↓(∪fms)∘.≡fms         ⍝ selection mask per unique var.
            tovals←masks/¨⊂strip¨tos    ⍝ values per unique var.
            1∧.≥⊃∘⍴∘∪¨tovals            ⍝ ok if unique vals per var.
        }¨⍵/pats                        ⍝ on each selected patterns.
    }                                   ⍝ hits ← expr :: hits

    repl←{                              ⍝ ⍺-transform expression ⍵.
        fm to←⍺                         ⍝ from and to patterns.
        fms tos←↓⍉↑fm dict ⍵            ⍝ make substitution dictionary.
        0{                              ⍝ substitute from dictionary.
            1≠≡,⍵:0 1 0 ∇¨⍵             ⍝ expr: substitute sub-expressions.
            (fms⍳⊂⍵)⊃tos,⊂⍺⊃⍵(⍵ ¯1)     ⍝ leaf: substitute from dictionary.
        }to                             ⍝ using "to" pattern.
    }                                   ⍝ expr ← (fm to) :: expr

    dict←{                              ⍝ dictionary from pattern.
        1=≡,⍺:⊂⍺ ⍵                      ⍝ leaf: (fm to) pair
        ↑,/⍺ ∇¨⍵                        ⍝ expr: collected sub-expr pairs.
    }

    (⊃¨rules)trav ⍵                     ⍝ patterns to reduce expression.
}

────────────────────────────────────────────────────────────────────────────────

show←{⎕IO ⎕ML←0                             ⍝ Show expression tree.

    0≡⊃0⍴⊂⍵:⍵                               ⍝ raw number: finished.
    1=≡,⍵:⍵                                 ⍝ simple: finished.
    2∧.=↑⍴¨⍵:⍵                              ⍝ defs: finished.

    sfmt←0∘{                                ⍝ standard format.
        '∘'≡⍵:''                            ⍝ null: ignore.
        lp rp←⍺/¨'()'                       ⍝ outer parens.
        1=≡,⍵:lp,(⍕⍵),rp                    ⍝ simple: format.
        2=⍴⍵:⍺ ∇⊃⍵                          ⍝ fn pos: format fn.
        lft fn rgt←⍵                        ⍝ split expression.
        func←{(1=≡,⍵)⊃(,⊃1↓⍵)''}            ⍝ expr's function, or ''.
        prop←{(fns⍳⊂func ⍵)⊃⍺,⍴⍺}           ⍝ fn's ⍺-property.
        ls os rs←strs∘prop¨lft ⍵ rgt        ⍝ binding strengths,
        as←ascs∘prop¨lft rgt                ⍝ association orders.
        pars←(ls rs<os)∨(ls rs=os)∧as='→←'  ⍝ sub-parens needed.
        over←1,~'∘'≡⊃rgt                    ⍝ monadic right expr override.
        name←0,(lft≡'∘')∧1<⍴fn              ⍝ monadic "named" fn needs pars.
        lv rv←(over∧pars∨name)∇¨lft rgt     ⍝ format left and right subs.
        lp,lv,fn,rv,rp                      ⍝ linear format of expression.
    }

    pfmt←{                                  ⍝ fully parenthesised linear.
        '∘'≡⍵:⍵                             ⍝ null: ignore.
        0=≡⍵:⍕⍵                             ⍝ number: format.
        1=≡⍵:⍵                              ⍝ atom: done
        lft fn rgt←strip ⍵                  ⍝ split expression.
        lft≡'∘':'(',fn,(∇ rgt),')'          ⍝ monadic function:
        '(',(∇ lft),fn,(∇ rgt),')'          ⍝ parenthesised expression.
    }

    tfmt←{                                  ⍝ tree format.
        '∘'≡⍵:0 0⍴''                        ⍝ null: ignore.
        0=≡⍵:↑,↓,⍕⍵                         ⍝ number: format.
        1=≡⍵:↑,↓⍵                           ⍝ atom: done.
        lft fn rgt←strip ⍵                  ⍝ split expr.
        subs←{' ',⍵}\∇¨lft rgt              ⍝ format subtrees.
        tops←<\¨' '≠{⌽⍵}\⊃∘↓¨subs           ⍝ connection points
        line←{(⍵++\⍵)⊃¨⊂' ─',⍺}             ⍝ line drawing chars.
        tee←('∘'≡⊃⍵)⊃'┴└'                   ⍝ dyadic/monadic tee piece.
        decr←{tee,1↓⌽⍵}\'┌┐'line¨tops       ⍝ top decoration.
        subm←↑,/↑¨↓↑↓¨decr⍪¨subs            ⍝ formatted sub-matrix.
        tabs←(⍴⊃tops)/' '                   ⍝ function alignment.
        ↑(⊂tabs,fn),↓subm                   ⍝ formatted expr tree.
    }

    strs ascs fns _←defns                   ⍝ strengths, assocs, functions.

    ⍺←0                                     ⍝ default: standard format.
    ⍺∊'s' 0:sfmt strip ⍵                    ⍝ standard format.
    ⍺∊'p' 1:pfmt strip ⍵                    ⍝ fully parenthesised.
    ⍺∊'t' 2:tfmt strip ⍵                    ⍝ tree format.
}

────────────────────────────────────────────────────────────────────────────────

test←{                                          ⍝ Check test cases.
    ⍺←0 ⋄ progress←⍺∘{⍺:{0}⎕←⍵ ⋄ 0}             ⍝ ⍺: show progress.
    ⍵≡'':⍺∘∇∘⍎¨↓⎕NL 2                           ⍝ '' => all scripts.
    (⊂⍵)∊~∘' '¨↓⎕NL 2:⍺ ∇⍎⍵                     ⍝ test'script' → test script.
    ~(⊂⍵)∊⍎¨↓⎕NL 2:⍵,'?'                        ⍝ bad script name.
    progress⊃↓⎕FMT ⍵:                           ⍝ show first line of script.
    match←{z←eval ⍺ ⋄ z is ⍵:1 ⋄ ⍺ show z}      ⍝ display unexpected result.
    error←{666::chkerr ⍵ ⋄ {0}⎕←eval ⍺}         ⍝ confirm expected errors.
    chkerr←{⍵≡trimerr⊃⎕DM:1 ⋄ {0}⎕←⊃⎕DM}        ⍝ check expected error matches.
    trimerr←{(2++/∧\⍵≠'\')↓⍵}                   ⍝ trim posn info from error msg.
    trim←{(+/∧\' '=⍵)↓⍵}{⌽⍺⍺⌽⍺⍺ ⍵}              ⍝ trim leading & trailing blanks
    show←{{0}⎕←⍺,' -> ',⍵}                      ⍝ display erroneous result.
    split←{trim¨0 2↓¨(1,1↓⍺⍷⍵)⊂⍵}               ⍝ expr and expected result.
    is←{⍺≡⍵:1 ⋄ 0≠⊃0⍴⍺:0 ⋄ ⍺≡⍎⍵}                ⍝ match expr or number.
    save←exp.(defns rules pvars pchks)          ⍝ save current rules.
    _←load ⍵                                    ⍝ install rules.
    oks←{                                       ⍝ ok vector.
        progress ⍵:                             ⍝ optionally, show progress.
        1∊'->'⍷⍵:↑match/'->'split ⍵             ⍝ expect match.
        1∊'!!'⍷⍵:↑error/'!!'split ⍵             ⍝ expect error.
    }¨{                                         ⍝ test script:
        cmat←⎕FMT↑⍵                             ⍝ char matrix script.
        comm←'⍝'=⊃¨~∘' '¨↓cmat                  ⍝ lines starting with '⍝'
        test←∨/↑∨/'->' '!!'⍷¨⊂cmat              ⍝ test lines.
        dlc←{(⍵⍳'⍝')↓⍵}                         ⍝ drop leading '⍝' char.
        dtc←{(∧\⍵≠'⍝')/⍵}                       ⍝ drop trailing comment.
        dtc∘dlc¨(comm∧test)/↓cmat               ⍝ test vector.
    }⍵                                          ⍝ for given rule-set.
    exp.(defns rules pvars pchks)←save          ⍝ restore prevailing rules.
    1:rslt←oks                                  ⍝ shy result vector.
}

────────────────────────────────────────────────────────────────────────────────


Back to: Contents