// Keyword.bcpl -- main portion of keyword table package, // containing primitives to look up keywords and to // enumerate and destroy keyword tables. // Copyright Xerox Corporation 1979, 1982 // Last modified May 13, 1982 3:55 PM by Taft get "Keyword.decl" external [ // outgoing procedures KTLookup; KTEnumerate; KTDestroy; BinarySearch; CompareKey // incoming procedures StringCompare; Free; Max ] //--------------------------------------------------------------------------- let KTLookup(kt, key, lvTableKey; numargs na) = valof //--------------------------------------------------------------------------- // Looks up key in kt and returns a pointer to the corresponding entry // if it is found, zero if not found. If the key is an initial substring of // exactly one entry it is considered found, but if it is an initial substring // of more than one entry it is considered not found. // If lvTableKey is provided, a pointer to the keyword string in the matching // entry is stored in @lvTableKey if the key is either found or ambiguous. // To clarify: // Condition: returns: @lvTableKey: // Exact match entry pointer matching table key // Unique initial substring entry pointer matching table key // Ambiguous initial substring zero first matching table key // No match zero zero [ if na ls 3 then lvTableKey = lv na let numEntries = kt>>KT.numEntries let r = BinarySearch(key, kt, numEntries, CompareKey) let i = Max(r, not r) // index of smallest entry ge key let kte = lv kt>>KT.kte↑0 + i * kt>>KT.lenKTE let entry = lv kte>>KTE.entry if r ls 0 then [ // not exact match, test for substring match if i ge numEntries % StringCompare(key, kte>>KTE.key) ne -2 then [ @lvTableKey = 0; resultis 0 ] // not substring match if i+1 ls numEntries & CompareKey(key, kt, i+1) eq -2 then entry = 0 // ambiguous ] @lvTableKey = kte>>KTE.key resultis entry ] //--------------------------------------------------------------------------- and KTEnumerate(kt, Proc, arg) be //--------------------------------------------------------------------------- // Calls Proc(entry, kt, keyword, arg) for each entry in the table. // Proc may change the entry but must not insert or delete keys. [ let kte = lv kt>>KT.kte↑0 let endKTE = kte + kt>>KT.numEntries * kt>>KT.lenKTE until kte eq endKTE do [ Proc(lv kte>>KTE.entry, kt, kte>>KTE.key, arg) kte = kte + kt>>KT.lenKTE ] ] //--------------------------------------------------------------------------- and KTDestroy(kt) be //--------------------------------------------------------------------------- // Destroys keyword table [ KTEnumerate(kt, FreeKey) Free(kt>>KT.zone, kt) ] //--------------------------------------------------------------------------- and FreeKey(entry, kt, keyword, nil) be Free(kt>>KT.zone, keyword) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and CompareKey(key, kt, i) = StringCompare(key, (lv kt>>KT.kte↑0 + i * kt>>KT.lenKTE)>>KTE.key) //--------------------------------------------------------------------------- // Compares key against the kt entry whose index is i. Returns a value // describing the outcome: // -2 the key is an initial substring of the entry // -1 the key is "less than" the entry but not an initial substring // 0 the key is "equal to" the entry // 1 the key is "greater than" the entry //--------------------------------------------------------------------------- and BinarySearch(key, tbl, lenTbl, Compare) = valof //--------------------------------------------------------------------------- // Searches for key in the sorted table tbl, which has entries numbered // zero to lenTbl-1. The comparison procedure Compare(key, tbl, i) is // expected to compare key against entry i in the table and return // a negative number if the key is "less than" the entry, zero if "equal", // or a positive number if "greater than". // If the requested key is found, BinarySearch returns the index of the // matching entry in the table. If not found, returns -i-1 (= not i), // where i is the index of the entry before which the key should be inserted. [ let low, high = 0, lenTbl-1 [ if high ls low resultis not low let probe = (low+high) rshift 1 let r = Compare(key, tbl, probe) if r eq 0 resultis probe test r ls 0 ifso high = probe-1 ifnot low = probe+1 ] repeat ]