## Test driver for entab and detab # # Input is read from standard input. Commentary and error reports go to # standard output. # # Input lines are first preprocessed by interpreting escape sequences \a, \b, # \n, \r, and \t and trimming a trailing '$' character. # # Input lines beginning with "=" establish tab stop settings. Each numeric # field specifies a tab stop, according to the entab/detab specs. # # All other lines are passed through entab and then detab, and the results are # checked. The characters "!" and "." are replaced by spaces before calling # entab; "!" positions are expected to be replaced by tabs, with "." positions # disappearing. For example, "abcd!...ijk" tests that entab("abcd ijk") # returns "abcd\tijk". # # The result of each entab call is then passed to detab, with results expected # to match the original entab argument (or its detab, if it had any tabs). procedure main () params := setup ("=") # start with default tabs (no args) while line := escape (read ()) do { # read and preprocess line if line[1] == "=" then params := setup (line) # '=' line sets tab stops (arg list) else { s := map (line, "!.", " ") # turn "!." characters into spaces params[1] := s t := invoke (entab, params) # run entab if t ~== interp (line) then { # check results write ("entab failed for: ", map(line,"\t\r\n\b\007","!RNBA")) write (" returned value: ", map(t, "\t\r\n\b\007","!RNBA")) } else { if upto ('\t', s) then # detab input if it had a tab s := invoke (detab, params) params[1] := t t := invoke (detab, params) # detab the result of the entab if t ~== s then { # compare results write ("detab failed for: ", map(line,"\t\r\n\b\007","!RNBA")) write (" returned value: ", map(t, "\t\r\n\b\007","!RNBA")) } } } } end procedure escape (line) # interpret escape sequences and trim one '$' if line[-1] == "$" then line := line[1:-1] s := "" line ? while not pos (0) do { s ||:= tab (upto ('\\') | 0) s ||:= (="\\" & case (c := move(1)) of { "a": "\007" "b": "\b" "n": "\n" "r": "\r" "t": "\t" default: "\\" || c }) } return s end procedure interp (pattern) # interpret metacharacters '!.' s := "" pattern ? while not pos (0) do { tab (many ('.')) s ||:= tab (upto ('.') | 0) } return map (s, "!", "\t") end procedure setup (line) # interpret and report a column spec line p := [&null] line ? while tab (upto (&digits)) do put (p, integer (tab (many (&digits)))) writes ("testing entab/detab(s") every writes (",", \!p) write (")") return p end procedure invoke (func, a) # invoke a function with a list of up to 10 args return case *a of { 0: func () 1: func (a[1]) 2: func (a[1], a[2]) 3: func (a[1], a[2], a[3]) 4: func (a[1], a[2], a[3], a[4]) 5: func (a[1], a[2], a[3], a[4], a[5]) 6: func (a[1], a[2], a[3], a[4], a[5], a[6]) 7: func (a[1], a[2], a[3], a[4], a[5], a[6], a[7]) 8: func (a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) 9: func (a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]) 10: func (a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10]) default: stop ("too many args for invoke") } end