// KeywordInit.bcpl -- initialization for keyword table package,
//		containing primitives for creating keyword tables
//		and inserting and deleting keyword table entries.
// Copyright Xerox Corporation 1979, 1982

// Last modified May 12, 1982  5:24 PM by Taft

get "Keyword.decl"

external
[
// outgoing procedures
CreateKeywordTable; DestroyKeywordTable; EnumerateKeywordTable
LookupKeyword; InsertKeyword; DeleteKeyword

// for OEP declaration only
KTInsert; KTDelete

// incoming procedures
KTLookup; KTEnumerate; KTDestroy
BinarySearch; CompareKey; ExtractSubstring
DefaultArgs; Allocate; Free; MoveBlock; Zero; SysErr
Call0; Call1; Call2; Call3; Call4

// incoming statics
sysZone
]

static
[
DestroyKeywordTable; EnumerateKeywordTable
LookupKeyword; InsertKeyword; DeleteKeyword
]

structure String: [ length byte; char↑1,255 byte ]

//---------------------------------------------------------------------------
let CreateKeywordTable(maxEntries, lenEntry, zone; numargs na) = valof
//---------------------------------------------------------------------------
// Creates and returns a keyword table capable of holding a maximum of
// maxEntries entries of lenEntry words each
[
DefaultArgs(lv na, -1, 1, sysZone)
let lenKTE = offset KTE.entry/16 + lenEntry
let kt = Allocate(zone, (offset KT.kte↑0)/16 + maxEntries*lenKTE)
kt>>KT.Insert = KTInsert
kt>>KT.Delete = KTDelete
kt>>KT.Lookup = KTLookup
kt>>KT.Enumerate = KTEnumerate
kt>>KT.Destroy = KTDestroy
kt>>KT.zone = zone
kt>>KT.numEntries = 0
kt>>KT.maxEntries = maxEntries
kt>>KT.lenKTE = lenKTE
InsertKeyword = Call0
DeleteKeyword = Call1
LookupKeyword = Call2
EnumerateKeywordTable = Call3
DestroyKeywordTable = Call4
resultis kt
]

//---------------------------------------------------------------------------
and KTInsert(kt, key) = valof
//---------------------------------------------------------------------------
// Inserts a copy of the supplied key string into the table kt
// and returns a pointer to the new entry (which has been zeroed).
[
let i = not BinarySearch(key, kt, kt>>KT.numEntries, CompareKey)
if i ls 0 then SysErr(kt, ecKeyAlreadyExists)
if kt>>KT.numEntries eq kt>>KT.maxEntries then SysErr(kt, ecKeyTableFull)
let kte = GetKTE(kt, i)
let p = GetKTE(kt, kt>>KT.numEntries)-1
while p ge kte do [ p!(kt>>KT.lenKTE) = p!0; p = p-1 ]
kt>>KT.numEntries = kt>>KT.numEntries+1
Zero(kte, kt>>KT.lenKTE)
kte>>KTE.key = ExtractSubstring(key, 0, 0, kt>>KT.zone)
resultis lv kte>>KTE.entry
]

//---------------------------------------------------------------------------
and KTDelete(kt, key) be
//---------------------------------------------------------------------------
// Deletes the entry matching key from the table.
[
let i = BinarySearch(key, kt, kt>>KT.numEntries, CompareKey)
if i ls 0 then SysErr(kt, ecKeyDoesntExist)
let kte = GetKTE(kt, i)
Free(kt>>KT.zone, kte>>KTE.key)
kt>>KT.numEntries = kt>>KT.numEntries-1
// this may move zero words, but that's supposed to be ok
MoveBlock(kte, kte+kt>>KT.lenKTE, (kt>>KT.numEntries-i)*kt>>KT.lenKTE)
]

//---------------------------------------------------------------------------
and GetKTE(kt, i) = lv kt>>KT.kte↑0 + i * kt>>KT.lenKTE
//---------------------------------------------------------------------------