A partial APL parser

Summary

Richard Suchenwirth: This is part 3 of the series that began with Playing APL and APLish - a partial parser apl2t that converts a string in sub-APL to a string that can be eval'ed by Tcl (call apl).

It is partial in two ways: it does not attempt to build a complete parse tree, and it does not cover more than a tiny subset of APL, but some already:

% apl2t {N N rho 1,N rho 0}
rho "$N $N" [, "1" [rho "$N" "0" ]]
% set N 5
5
% apl {N N rho 1,N rho 0}
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
% apl N+2*0.5
6.414213562373095

Still a lot to do (quoted strings, compound operators like -:..), but it's tremendous fun...;-)

namespace eval APL {
    namespace export apl apl2t

    proc apl s {uplevel [apl2t $s]}

    proc apl2t list {
    set res ""
    #---------- insert potential blanks everywhere, then reduce
    set list [join [split $list ""] \x81]
    foreach i {1 2} {
        regsub -all "(\[_A-Z0-9\])\x81(\[_A-Z0-9\])" $list {\1\2} list
        regsub -all "(\[a-z0-9\])\x81(\[a-z0-9\])" $list {\1\2} list
    }
    regsub -all "(\[.\])\x81(\[0-9\])" $list {\1\2} list
    regsub -all "(\[0-9\])\x81(\[.\])" $list {\1\2} list
    regsub -all \x81 $list " " list
    set op ""
    set last ""
    #---------- walk the list from back
    for {set n [llength $list]} {$n>0} {incr n -1} {
        set it [lindex $list [expr $n-1]]
        if [regexp {^[A-Z_]} $it] {
        if {$last!="val"} {set it "$it\""}
        set res "\$$it $res"
        set last val
        } elseif {[regexp {^-?[0-9.]} $it]} {
        if {$last!="val"} {set it $it\"}
        set res "$it $res"
        set last val
        } elseif {$it==")"} {
        if {$last=="val"} {set res \"$res}
        set last embed
        set open [lsearch $list "("]
        if {$open<0} {error "unmatched paren"}
        set embed [lrange $list [incr open] [incr n -2]]
        set res "\[[apl2t $embed]\] $res"
        set n $open
        } else {
        if {$last=="val"} {set res \"$res}
        set last op
        if [llength $op] {
            set res "$op $res"; set op ""
            if {$n>2} {set res "\[$res\]"}
        }
        set op $it
        }
    }
    if {$last=="val"} {set res \"$res}
    if [llength $op] {set res "$op $res"}
    set res
    }
}