Blame test/xref.original

Packit 575503
XREF(AWK)                   Philip L. Bewig                   XREF(AWK)
Packit 575503
Packit 575503
NAME
Packit 575503
Packit 575503
        xref(awk) - produce a cross reference listing of an awk program
Packit 575503
Packit 575503
SYNOPSIS
Packit 575503
Packit 575503
        awk -f xref.awk [ file ... ]
Packit 575503
Packit 575503
DESCRIPTION
Packit 575503
Packit 575503
        XREF(AWK) takes as input a valid awk program and produces as out-
Packit 575503
        put a cross-reference listing of all variables and function calls
Packit 575503
        which appear in the program.
Packit 575503
Packit 575503
        For ordinary variables and array variables, a line of the form
Packit 575503
Packit 575503
                count var(func) lines ...
Packit 575503
Packit 575503
        is produced, where "count" is the number of times the variable is
Packit 575503
        used, "var" is the name of the variable, "func" is the function
Packit 575503
        name to which the variable is local (a null "func" indicates that
Packit 575503
        the variable is global), and "lines" is the number of each line
Packit 575503
        where the variable appears.  Appearances of the variable in a
Packit 575503
        function's parameter list are ignored.  The number of lines shown
Packit 575503
        may differ from "count" if the variable appears more than once on
Packit 575503
        the same line.
Packit 575503
Packit 575503
        For functions, a line of the form
Packit 575503
Packit 575503
                count func(define) lines ...
Packit 575503
Packit 575503
        is produced, where "count" is the number of times the function is
Packit 575503
        called, "func" is the name of the function, "define" is the lime
Packit 575503
        number where the function is defined, and "lines" is the number of
Packit 575503
        each line where the function is called.  As for variables, the
Packit 575503
        number of lines shown may differ from "count."
Packit 575503
Packit 575503
        Output lines for variables and functions are intermixed and are
Packit 575503
        sorted by name.  Though terse, the output is informative, easy to
Packit 575503
        read, and amenable to further processing.
Packit 575503
Packit 575503
EXAMPLE
Packit 575503
Packit 575503
        The cross-reference listing produced by running xref.awk against
Packit 575503
        itself is shown below:
Packit 575503
Packit 575503
                5 NR() 39 45 50 53 68
Packit 575503
                8 RLENGTH() 119 120 123 124 127 128 132 133
Packit 575503
                10 act() 31 34 36 40 51 54 56 63 65 67
Packit 575503
                1 arr(asplit) 90
Packit 575503
                2 arr(inarray) 93 94
Packit 575503
                1 asplit(89) 6
Packit 575503
                3 braces() 55 57 58
Packit 575503
                2 flines() 53 79
Packit 575503
                1 fs(asplit) 89
Packit 575503
                3 funcname() 42 52 61
Packit 575503
                16 i() 76 77 78 79 81 82 83 84 90
Packit 575503
                3 inarray(92) 37 43 48
Packit 575503
                6 j() 82 83 84
Packit 575503
                3 j(inarray) 93 94
Packit 575503
                3 keywords() 10 125 129
Packit 575503
                1 lex(97) 29
Packit 575503
                31 line() 103 104 107 108 109 110 112 113 114 115 116 117 118
Packit 575503
                        119 120 122 123 124 126 127 128 131 132 133
Packit 575503
                6 lines() 39 45 50 83 84
Packit 575503
                4 local() 41 59 60 64
Packit 575503
                3 machine() 17 30 31
Packit 575503
                2 n(asplit) 89 90
Packit 575503
                15 names() 37 38 43 44 48 49 77 78 79 81 82 83 84
Packit 575503
                3 nextstate() 30 62 73
Packit 575503
                4 nnames() 38 44 49 76
Packit 575503
                4 state() 23 30 31 73
Packit 575503
                1 str(asplit) 89
Packit 575503
                3 symb() 29 30 31
Packit 575503
                2 temp() 59 60
Packit 575503
                2 temp_asplit() 89 90
Packit 575503
                31 tok() 37 38 39 41 42 43 44 45 47 48 49 50 52 53 64 101 105
Packit 575503
                        113 115 117 119 123 125 127 129 132
Packit 575503
                1 val(inarray) 94
Packit 575503
                5 xnames() 39 45 50 77 82
Packit 575503
Packit 575503
        For readability, some lines have been folded.
Packit 575503
Packit 575503
SOURCE CODE
Packit 575503
Packit 575503
        # xref.awk - cross reference an awk program
Packit 575503
Packit 575503
        BEGIN {
Packit 575503
Packit 575503
                # create array of keywords to be ignored by lexer
Packit 575503
                asplit("BEGIN:END:atan2:break:close:continue:cos:delete:" \
Packit 575503
                        "do:else:exit:exp:for:getline:gsub:if:in:index:int:"  \
Packit 575503
                        "length:log:match:next:print:printf:rand:return:sin:" \
Packit 575503
                        "split:sprintf:sqrt:srand:sub:substr:system:while",
Packit 575503
                        keywords,":")
Packit 575503
Packit 575503
                # build the symbol-state table
Packit 575503
                split("00:00:00:00:00:00:00:00:00:00:" \
Packit 575503
                          "20:10:10:12:12:11:07:00:00:00:" \
Packit 575503
                          "08:08:08:08:08:33:08:00:00:00:" \
Packit 575503
                          "08:44:08:36:08:08:08:00:00:00:" \
Packit 575503
                          "08:44:45:42:42:41:08",machine,":")
Packit 575503
Packit 575503
                # parse the input and store an intermediate representation
Packit 575503
                # of the cross-reference information
Packit 575503
Packit 575503
                # set up the machine
Packit 575503
                state = 1
Packit 575503
Packit 575503
                # run the machine
Packit 575503
                for (;;) {
Packit 575503
Packit 575503
                        # get next symbol
Packit 575503
                        symb = lex()
Packit 575503
                        nextstate = substr(machine[state symb],1,1)
Packit 575503
                        act = substr(machine[state symb],2,1)
Packit 575503
Packit 575503
                        # perform required action
Packit 575503
                        if ( act == "0" )
Packit 575503
                                ; # do nothing
Packit 575503
                        else if ( act == "1" ) {
Packit 575503
                                if ( ! inarray(tok,names) )
Packit 575503
                                        names[++nnames] = tok
Packit 575503
                                lines[tok,++xnames[tok]] = NR }
Packit 575503
                        else if ( act == "2" ) {
Packit 575503
                                if ( tok in local ) {
Packit 575503
                                        tok = tok "(" funcname ")"
Packit 575503
                                        if ( ! inarray(tok,names) )
Packit 575503
                                                names[++nnames] = tok
Packit 575503
                                        lines[tok,++xnames[tok]] = NR }
Packit 575503
                                else {
Packit 575503
                                        tok = tok "()"
Packit 575503
                                        if ( ! inarray(tok,names) )
Packit 575503
                                                names[++nnames] = tok
Packit 575503
                                        lines[tok,++xnames[tok]] = NR } }
Packit 575503
                        else if ( act == "3" ) {
Packit 575503
                                funcname = tok
Packit 575503
                                flines[tok] = NR }
Packit 575503
                        else if ( act == "4" )
Packit 575503
                                braces++
Packit 575503
                        else if ( act == "5" ) {
Packit 575503
                                braces--
Packit 575503
                                if ( braces == 0 ) {
Packit 575503
                                        for ( temp in local )
Packit 575503
                                                delete local[temp]
Packit 575503
                                        funcname = ""
Packit 575503
                                        nextstate = 1 } }
Packit 575503
                        else if ( act == "6" ) {
Packit 575503
                                local[tok] = 1 }
Packit 575503
                        else if ( act == "7" )
Packit 575503
                                break
Packit 575503
                        else if ( act == "8" ) {
Packit 575503
                                print "error: xref.awk: line " NR ": aborting" \
Packit 575503
                                        > "/dev/con"
Packit 575503
                                exit 1 }
Packit 575503
Packit 575503
                        # finished with current token
Packit 575503
                        state = nextstate }
Packit 575503
Packit 575503
                # finished parsing, now ready to print output
Packit 575503
                for ( i = 1; i <= nnames; i++ ) {
Packit 575503
                        printf "%d ", xnames[names[i]] |"sort +1"
Packit 575503
                        if ( index(names[i],"(") == 0 )
Packit 575503
                                printf "%s(%d)", names[i], flines[names[i]] |"sort +1"
Packit 575503
                        else
Packit 575503
                                printf "%s", names[i] |"sort +1"
Packit 575503
                        for ( j = 1; j <= xnames[names[i]]; j++ )
Packit 575503
                                if ( lines[names[i],j] != lines[names[i],j-1] )
Packit 575503
                                        printf " %d", lines[names[i],j] |"sort +1"
Packit 575503
                        printf "\n" |"sort +1" }
Packit 575503
Packit 575503
                } # END OF PROGRAM
Packit 575503
Packit 575503
        function asplit(str,arr,fs,  n) { n = split(str,temp_asplit,fs)
Packit 575503
                for ( i = 1; i <= n; i++ ) arr[temp_asplit[i]]++ }
Packit 575503
Packit 575503
        function inarray(val,arr,  j) {
Packit 575503
            for ( j in arr )
Packit 575503
                if ( arr[j] == val ) return j
Packit 575503
            return "" }
Packit 575503
Packit 575503
        function lex() {
Packit 575503
Packit 575503
                for (;;) {
Packit 575503
Packit 575503
                        if ( tok == "(eof)" ) return 7
Packit 575503
Packit 575503
                        while ( length(line) == 0 )
Packit 575503
                                if ( getline line == 0 ) {
Packit 575503
                                        tok = "(eof)"; return 7 }
Packit 575503
Packit 575503
                        sub(/^[ \t]+/,"",line)                                # remove white space,
Packit 575503
                        sub(/^"([^"]|\\")*"/,"",line)             # quoted strings,
Packit 575503
                        sub(/^\/([^\/]|\\\/)+\//,"",line)     # regular expressions,
Packit 575503
                        sub(/^#.*/,"",line)                                   # and comments
Packit 575503
Packit 575503
                        if ( line ~ /^function/ ) {
Packit 575503
                                tok = "function"; line = substr(line,9); return 1 }
Packit 575503
                        else if ( line ~ /^{/ ) {
Packit 575503
                                tok = "{"; line = substr(line,2); return 2 }
Packit 575503
                        else if ( line ~ /^}/ ) {
Packit 575503
                                tok = "}"; line = substr(line,2); return 3 }
Packit 575503
                        else if ( match(line,/^[A-Za-z_][A-Za-z_0-9]*\[/) ) {
Packit 575503
                                tok = substr(line,1,RLENGTH-1)
Packit 575503
                                line = substr(line,RLENGTH+1)
Packit 575503
                                return 5 }
Packit 575503
                        else if ( match(line,/^[A-Za-z_][A-Za-z_0-9]*\(/) ) {
Packit 575503
                                tok = substr(line,1,RLENGTH-1)
Packit 575503
                                line = substr(line,RLENGTH+1)
Packit 575503
                                if ( ! ( tok in keywords ) ) return 6 }
Packit 575503
                        else if ( match(line,/^[A-Za-z_][A-Za-z_0-9]*/) ) {
Packit 575503
                                tok = substr(line,1,RLENGTH)
Packit 575503
                                line = substr(line,RLENGTH+1)
Packit 575503
                                if ( ! ( tok in keywords ) ) return 4 }
Packit 575503
                        else {
Packit 575503
                                match(line,/^[^A-Za-z_{}]/)
Packit 575503
                                tok = substr(line,1,RLENGTH)
Packit 575503
                                line = substr(line,RLENGTH+1) } } }
Packit 575503
Packit 575503
TECHNICAL DISCUSSION
Packit 575503
Packit 575503
        Broadly, XREF(AWK) parses an awk program using a symbol-state
Packit 575503
        table, in much the same way as a yacc-generated parser.  The
Packit 575503
        lexical analyzer recognizes seven distinct symbols:  the word
Packit 575503
        "function", the left brace, the right brace, identifiers used
Packit 575503
        as variables, identifiers used as arrays, identifiers used as
Packit 575503
        functions, and end of file.  The type of symbol is returned to
Packit 575503
        the parser as the value of the "lex" function, and the global
Packit 575503
        variable "tok" is set to the text of the current token.
Packit 575503
Packit 575503
        The symbol-state table is stored in the "machine" array.  The
Packit 575503
        table can be represented as follows:
Packit 575503
Packit 575503
                       symbol |     1       2  3   4     5     6     7
Packit 575503
                              |
Packit 575503
        state                 | "function"  {  }  var  array  func  eof
Packit 575503
        -- -- -- -- -- -- -- -+- -- -- -- -- -- -- -- -- -- -- -- -- --
Packit 575503
        1 any                 |     20     10  10  12    12    11   07
Packit 575503
        2 "function"          |     08     08  08  08    08    33   08
Packit 575503
        3 "function" name     |     08     44  08  36    08    08   08
Packit 575503
        4 "function" name "{" |     08     44  45  42    42    41   08
Packit 575503
Packit 575503
        where the first digit is the state to be entered after process-
Packit 575503
        ing the current token and the second digit is an action to be
Packit 575503
        performed.  The actions are listed below:
Packit 575503
Packit 575503
                1       found a function call
Packit 575503
                2       found a variable or array
Packit 575503
                3       found a function definition
Packit 575503
                4       found a left brace
Packit 575503
                5       found a right brace
Packit 575503
                6       found a local variable declaration
Packit 575503
                7       found end of file
Packit 575503
                8       found an error
Packit 575503
Packit 575503
        Each of the first six actions causes some information about the
Packit 575503
        target program to be stored for later processing; the structures
Packit 575503
        used will be discussed below.  The seventh action causes the
Packit 575503
        parser to exit.  The eighth action causes errors to be reported
Packit 575503
        to standard error and the program to abort.
Packit 575503
Packit 575503
        Before describing the intermediate data structures, we will
Packit 575503
        discuss some of the more interesting points in the action calls.
Packit 575503
        The "braces" variable keeps track of whether we are currently
Packit 575503
        within a functions; it is positive within a function and zero
Packit 575503
        without.  When the right brace which causes the value of "braces"
Packit 575503
        to go from one to zero is found, the value of "nextstate" is
Packit 575503
        changed from four (scanning a function) to one (any) and the
Packit 575503
        names of local variables are forgotten.  The "local" array is
Packit 575503
        accumulated from the variables found after the function name but
Packit 575503
        before the opening left brace of the function; action two care-
Packit 575503
        fully checks whether a variable is global or local before writing
Packit 575503
        to the intermediate data structure.  The variable "funcname" is
Packit 575503
        the name of the current function when within a function and null
Packit 575503
        without.
Packit 575503
Packit 575503
        The following arrays store an intermediate representation of the
Packit 575503
        variable and function identifiers of the target program:
Packit 575503
Packit 575503
                names[1..nnames] = list of all identifiers, both variable and
Packit 575503
                        function names; for variables, the name has the form
Packit 575503
                        var(func), but for functions, there are no parentheses
Packit 575503
Packit 575503
                xnames[names[i]] = number of times names[i] is used
Packit 575503
Packit 575503
                lines[names[i],1..xnames[names[i]]] = list of line numbers
Packit 575503
                        where names[i] is used
Packit 575503
Packit 575503
                flines[names[i]] = line number where function names[i] is
Packit 575503
                        defined
Packit 575503
Packit 575503
        These arrays are created as the parser reads the input; when the
Packit 575503
        parser is finished, the arrays are output in user-readable form.
Packit 575503
Packit 575503
PORTABILITY
Packit 575503
Packit 575503
        XREF(AWK) will work with any implementation of nawk.  The MKS
Packit 575503
        ToolKit implementation requires the large-model version of awk.
Packit 575503
Packit 575503
HISTORY
Packit 575503
Packit 575503
        Written by Phil Bewig on February 10, 1990.  Inspired by
Packit 575503
        Exercise 3-16 of the book "The Awk Programming Language" by
Packit 575503
        Alfred V. Aho, Brian W. Kernighan and Peter J. Weinberger
Packit 575503
        (Addison-Wesley:  1988).
Packit 575503
Packit 575503
COPYRIGHT
Packit 575503
Packit 575503
        This program is placed in the public domain.  However, the
Packit 575503
        author requests credit when distributed. 
Packit 575503