home *** CD-ROM | disk | FTP | other *** search
- ############################################################################
- #
- # File: complete.icn
- #
- # Subject: Procedure to complete partial input string
- #
- # Author: Richard L. Goerwitz
- #
- # Date: February 29, 1992
- #
- ###########################################################################
- #
- # Version: 1.7
- #
- ###########################################################################
- #
- # This file contains a single procedure, complete(s,st), which
- # completes a string (s) relative to a set or list of strings (st).
- # Put differently, complete() lets you supply a partial string, s,
- # and get back those strings in st that s is either equal to or a
- # substring of.
- #
- # Lots of command interfaces allow completion of partial input.
- # Complete() simply represents my personal sentiments about how this
- # might best be done in Icon. If you strip away the profuse comments
- # below, you end up with only about thirty lines of actual source
- # code.
- #
- # I have arranged things so that only that portion of an automaton
- # which is needed to complete a given string is actually created and
- # stored. Storing automata for later use naturally makes complete()
- # eat up more memory. The performance gains can make it worth the
- # trouble, though. If, for some reason, there comes a time when it
- # is advisable to reclaim the space occupied by complete's static
- # structures, you can just call it without arguments. This
- # "resets" complete() and forces an immediate garbage collection.
- #
- # Example code:
- #
- # commands := ["run","stop","quit","save","load","continue"]
- # while line := read(&input) do {
- # cmds := list()
- # every put(cmds, complete(line, commands))
- # case *cmds of {
- # 0 : input_error(line)
- # 1 : do_command(cmds[1])
- # default : display_possible_completions(cmds)
- # }
- # etc...
- #
- # More Iconish methods might include displaying successive
- # alternatives each time the user presses the tab key (this would,
- # however, require using the nonportable getch() routine). Another
- # method might be to use the first string suspended by complete().
- #
- # NOTE: This entire shebang could be replaced with a slightly slower
- # and much smaller program suggested to me by Jerry Nowlin and Bob
- # Alexander.
- #
- # procedure terscompl(s, st)
- # suspend match(s, p := !st) & p
- # end
- #
- # This program will work fine for lists with just a few members, and
- # also for cases where s is fairly large. It will also use much less
- # memory.
- #
- ############################################################################
- #
- # Links: none
- #
- ############################################################################
-
-
-
- procedure complete(s,st)
-
- local dfstn, c, l, old_chr, chr, newtbl, str, strset
- static t
- initial t := table()
-
- # No-arg invocation wipes out static structures & causes an
- # immediate garbage collection.
- if /s & /st then {
- t := table()
- collect() # do it NOW
- fail
- }
- type(st) == ("list"|"set") |
- stop("error (complete): list or set expected for arg2")
-
- # Seriously, all that's being done here is that possible states
- # are being represented by sets containing possible completions of
- # s relative to st. Each time a character is snarfed from s, we
- # check to see what strings in st might represent possible
- # completions, and store these in yet another set. At some
- # point, we either run into a character in s that makes comple-
- # tion impossible (fail), or we run out of characters in s (in
- # which case we succeed, & suspend each of the possible
- # completions).
-
- # Store any sets we have to create in a static structure for later
- # re-use.
- /t[st] := table()
-
- # We'll call the table entry for the current set dfstn. (It really
- # does enable us to do things deterministically.)
- dfstn := t[st]
-
- # Snarf one character at a time from s.
- every c := !s do {
-
- # The state we're in is represented by the set of all possible
- # completions before c was read. If we haven't yet seen char
- # c in this state, run through the current-possible-completion
- # set, popping off the first character of each possible
- # completion, and then construct a table which uses these
- # initial chars as keys, and makes the completions that are
- # possible for each of these characters into the values for
- # those keys.
- if /dfstn[st] then {
-
- # To get strings that start with the same char together,
- # sort the current string set (st).
- l := sort(st)
- newtbl := table()
- old_chr := ""
- # Now pop off each member of the sorted string set. Use
- # first characters as keys, and then divvy up the full strings
- # into sets of strings having the same initial letter.
- every str := !l do {
- str ? { chr := move(1) | next; str := tab(0) }
- if old_chr ~==:= chr then {
- strset := set([str])
- insert(newtbl, chr, strset)
- }
- else insert(strset, str)
- }
- insert(dfstn, st, newtbl)
- }
-
- # What we've done essentially is to create a table in which
- # the keys represent labeled arcs out of the current state,
- # and the values represent possible completion sets for those
- # paths. What we need to do now is store that table in dfstn
- # as the value of the current state-set (i.e. the current
- # range of possible completions). Once stored, we can then
- # see if there is any arc from the current state (dfstn[st])
- # with the label c (dfstn[st][c]). If so, its value becomes
- # the new current state (st), and we cycle around again for
- # yet another c.
- st := \dfstn[st][c] | fail
- if *st = 1 & match(s,!st)
- then break
- }
-
- # Eventually we run out of characters in c. The current state
- # (i.e. the set of possible completions) can simply be suspended
- # one element at a time, with s prefixed to each element. If, for
- # instance, st had contained ["hello","help","hear"] at the outset
- # and s was equal to "hel", we would now be suspending "hel" ||
- # !set(["lo","p"]).
- suspend s || !st
-
- end
-