1-- Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd. 2-- All rights reserved. 3-- 4-- Redistribution and use in source and binary forms, with or without 5-- modification, are permitted provided that the following conditions are 6-- met: 7-- 8-- - Redistributions of source code must retain the above copyright 9-- notice, this list of conditions and the following disclaimer. 10-- 11-- - Redistributions in binary form must reproduce the above copyright 12-- notice, this list of conditions and the following disclaimer in 13-- the documentation and/or other materials provided with the 14-- distribution. 15-- 16-- - Neither the name of The Numerical ALgorithms Group Ltd. nor the 17-- names of its contributors may be used to endorse or promote products 18-- derived from this software without specific prior written permission. 19-- 20-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 21-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 24-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32)package "BOOT" 33 34DEFPARAMETER($getUnexposedOperations, true) 35 36--% Functions for manipulating MODEMAP DATABASE 37 38augLisplibModemapsFromCategory(form is [op,:argl], body, signature, 39 domainShell) == 40 sl := [["$",:"*1"],:[[a,:p] for a in argl 41 for p in rest $PatternVariableList]] 42 form:= SUBLIS(sl,form) 43 body:= SUBLIS(sl,body) 44 signature:= SUBLIS(sl,signature) 45 opAlist:= SUBLIS(sl, domainShell.(1)) or return nil 46 nonCategorySigAlist:= 47 mkAlistOfExplicitCategoryOps substitute("*1","$",body) 48 domainList:= 49 [[a,m] for a in rest form for m in rest signature | 50 isCategoryForm(m)] 51 catPredList:= [['ofCategory,:u] for u in [["*1",form],:domainList]] 52 for (entry:= [[op,sig,:.],pred,sel]) in opAlist | 53 member(sig,LASSOC(op,nonCategorySigAlist)) repeat 54 pred':= MKPF([pred,:catPredList],'AND) 55 modemap:= [["*1",:sig],[pred',sel]] 56 $lisplibModemapAlist:= 57 [[op,:interactiveModemapForm modemap],:$lisplibModemapAlist] 58 59augmentLisplibModemapsFromFunctor(form,opAlist,signature) == 60 form:= [formOp,:argl]:= formal2Pattern form 61 opAlist:= formal2Pattern opAlist 62 signature:= formal2Pattern signature 63 nonCategorySigAlist:= 64 mkAlistOfExplicitCategoryOps first signature or return nil 65 for (entry:= [[op,sig,:.],pred,sel]) in opAlist | 66 or/[(sig in catSig) for catSig in 67 allLASSOCs(op,nonCategorySigAlist)] repeat 68 skip:= 69 argl and CONTAINED("$",rest sig) => 'SKIP 70 nil 71 sel:= substitute(form,"$",sel) 72 patternList:= listOfPatternIds sig 73 --get relevant predicates 74 predList:= 75 [[a,m] for a in argl for m in rest signature 76 | MEMQ(a,$PatternVariableList)] 77 sig:= substitute(form,"$",sig) 78 pred':= MKPF([pred,:[mkDatabasePred y for y in predList]],'AND) 79 l:=listOfPatternIds predList 80 if "OR"/[null MEMQ(u,l) for u in argl] then 81 sayMSG ['"cannot handle modemap for",:bright op, 82 '"by pattern match" ] 83 skip:= 'SKIP 84 modemap:= [[form,:sig],[pred',sel,:skip]] 85 $lisplibModemapAlist:= [[op,:interactiveModemapForm modemap], 86 :$lisplibModemapAlist] 87 88saveUsersHashTable() == 89 erase_lib(['USERS, 'DATABASE]) 90 stream:= writeLib('USERS,'DATABASE) 91 for k in MSORT HKEYS $usersTb repeat 92 rwrite(k, HGET($usersTb, k), stream) 93 RSHUT stream 94 95saveDependentsHashTable() == 96 erase_lib(['DEPENDENTS, 'DATABASE]) 97 stream:= writeLib('DEPENDENTS,'DATABASE) 98 for k in MSORT HKEYS $depTb repeat 99 rwrite(k, HGET($depTb, k), stream) 100 RSHUT stream 101 102save_browser_data() == 103 buildLibdb(false) 104 dbSplitLibdb() 105 mkUsersHashTable() 106 saveUsersHashTable() 107 mkDependentsHashTable() 108 saveDependentsHashTable() 109 110getUsersOfConstructor(con) == 111 stream := readLib('USERS, 'DATABASE) 112 val := rread_list(con, stream) 113 RSHUT stream 114 val 115 116getDependentsOfConstructor(con) == 117 stream := readLib('DEPENDENTS, 'DATABASE) 118 val := rread_list(con, stream) 119 RSHUT stream 120 val 121 122orderPredicateItems(pred1,sig,skip) == 123 pred:= signatureTran pred1 124 pred is ["AND",:l] => orderPredTran(l,sig,skip) 125 pred 126 127orderPredTran(oldList,sig,skip) == 128 lastPreds:=nil 129 --(1) make two kinds of predicates appear last: 130 ----- (op *target ..) when *target does not appear later in sig 131 ----- (isDomain *1 ..) 132 for pred in oldList repeat 133 ((pred is [op,pvar,.] and MEMQ(op,'(isDomain ofCategory)) 134 and pvar=first sig and not (pvar in rest sig)) or 135 (not skip and pred is ['isDomain,pvar,.] and pvar="*1")) => 136 oldList:=delete(pred,oldList) 137 lastPreds:=[pred,:lastPreds] 138--sayBrightlyNT "lastPreds=" 139--pp lastPreds 140 141 --(2a) lastDependList=list of all variables that lastPred forms depend upon 142 lastDependList := "UNIONQ"/[listOfPatternIds x for x in lastPreds] 143--sayBrightlyNT "lastDependList=" 144--pp lastDependList 145 146 --(2b) dependList=list of all variables that isDom/ofCat forms depend upon 147 dependList := 148 "UNIONQ"/[listOfPatternIds y for x in oldList | 149 x is ['isDomain,.,y] or x is ['ofCategory,.,y]] 150--sayBrightlyNT "dependList=" 151--pp dependList 152 153 --(3a) newList= list of ofCat/isDom entries that don't depend on 154 for x in oldList repeat 155 if (x is ['ofCategory,v,body]) or (x is ['isDomain,v,body]) then 156 indepvl:=listOfPatternIds v 157 depvl:=listOfPatternIds body 158 else 159 indepvl := listOfPatternIds x 160 depvl := nil 161 (INTERSECTIONQ(indepvl,dependList) = nil) 162 and INTERSECTIONQ(indepvl,lastDependList) => 163 somethingDone := true 164 lastPreds := [:lastPreds,x] 165 oldList := delete(x,oldList) 166--if somethingDone then 167-- sayBrightlyNT "Again lastPreds=" 168-- pp lastPreds 169-- sayBrightlyNT "Again oldList=" 170-- pp oldList 171 172 --(3b) newList= list of ofCat/isDom entries that don't depend on 173 while oldList repeat 174 for x in oldList repeat 175 if (x is ['ofCategory,v,body]) or (x is ['isDomain,v,body]) then 176 indepvl:=listOfPatternIds v 177 depvl:=listOfPatternIds body 178 else 179 indepvl := listOfPatternIds x 180 depvl := nil 181 (INTERSECTIONQ(indepvl,dependList) = nil) => 182 dependList:= setDifference(dependList,depvl) 183 newList:= [:newList,x] 184-- sayBrightlyNT "newList=" 185-- pp newList 186 187 --(4) noldList= what is left over 188 (noldList:= setDifference(oldList,newList)) = oldList => 189-- sayMSG '"NOTE: Parameters to domain have circular dependencies" 190 newList := [:newList,:oldList] 191 return nil 192 oldList:=noldList 193-- sayBrightlyNT "noldList=" 194-- pp noldList 195 196 for pred in newList repeat 197 if pred is ['isDomain,x,y] or x is ['ofCategory,x,y] then 198 ids:= listOfPatternIds y 199 if and/[id in fullDependList for id in ids] then 200 fullDependList:= insertWOC(x,fullDependList) 201 fullDependList:= UNIONQ(fullDependList,ids) 202 203 newList:=[:newList,:lastPreds] 204 205--substitute (isDomain ..) forms as completely as possible to avoid false paths 206 newList := isDomainSubst newList 207 answer := [['AND,:newList],:INTERSECTIONQ(fullDependList,sig)] 208--sayBrightlyNT '"answer=" 209--pp answer 210 211isDomainSubst u == main where 212 main == 213 u is [head,:tail] => 214 nhead := 215 head is ['isDomain,x,y] => ['isDomain,x,fn(y,tail)] 216 head 217 [nhead,:isDomainSubst rest u] 218 u 219 fn(x,alist) == 220 atom x => 221 IDENTP x and MEMQ(x,$PatternVariableList) and (s := findSub(x,alist)) => s 222 x 223 [first x, :[fn(y, alist) for y in rest x]] 224 findSub(x,alist) == 225 null alist => nil 226 alist is [['isDomain,y,z],:.] and x = y => z 227 findSub(x,rest alist) 228 229signatureTran pred == 230 atom pred => pred 231 pred is ['has, D, catForm] and isCategoryForm(catForm) => 232 ['ofCategory,D,catForm] 233 [signatureTran p for p in pred] 234 235interactiveModemapForm mm == 236 -- create modemap form for use by the interpreter. This function 237 -- replaces all specific domains mentioned in the modemap with pattern 238 -- variables, and predicates 239 mm := replaceVars(COPY mm,$PatternVariableList,$FormalMapVariableList) 240 [pattern:=[dc,:sig],pred] := mm 241 pred := [fn x for x in pred] where fn x == 242 x is [a,b,c] and a ~= 'isFreeFunction and atom c => [a,b,[c]] 243 x 244--pp pred 245 [mmpat, patternAlist, partial, patvars] := 246 modemapPattern(pattern,sig) 247--pp [pattern, mmpat, patternAlist, partial, patvars] 248 [pred,domainPredicateList] := 249 substVars(pred,patternAlist,patvars) 250--pp [pred,domainPredicateList] 251 [pred,:dependList]:= 252 fixUpPredicate(pred,domainPredicateList,partial,rest mmpat) 253--pp [pred,dependList] 254 [cond, :.] := pred 255 [mmpat, cond] 256 257modemapPattern(mmPattern,sig) == 258 -- Returns a list of the pattern of a modemap, an Alist of the 259 -- substitutions made, a boolean flag indicating whether 260 -- the result type is partial, and a list of unused pattern variables 261 patternAlist := nil 262 mmpat := nil 263 patvars := $PatternVariableList 264 partial := false 265 for xTails in tails mmPattern repeat 266 x := first xTails 267 if x is ['Union,dom,tag] and tag = '"failed" and xTails=sig then 268 x := dom 269 partial := true 270 patvar := rassoc(x,patternAlist) 271 not null patvar => mmpat := [patvar,:mmpat] 272 patvar := first patvars 273 patvars := rest patvars 274 mmpat := [patvar,:mmpat] 275 patternAlist := [[patvar,:x],:patternAlist] 276 [NREVERSE mmpat,patternAlist,partial,patvars] 277 278substVars(pred,patternAlist,patternVarList) == 279 --make pattern variable substitutions 280 domainPredicates := nil 281 for [[patVar,:value],:.] in tails patternAlist repeat 282 pred := substitute(patVar,value,pred) 283 patternAlist := NSUBST(patVar, value, patternAlist) 284 domainPredicates := substitute(patVar,value,domainPredicates) 285 if not MEMQ(value,$FormalMapVariableList) then 286 domainPredicates := [["isDomain",patVar,value],:domainPredicates] 287 everything := [pred,patternAlist,domainPredicates] 288 for var in $FormalMapVariableList repeat 289 CONTAINED(var,everything) => 290 replacementVar := first patternVarList 291 patternVarList := rest patternVarList 292 pred := substitute(replacementVar,var,pred) 293 domainPredicates := substitute(replacementVar,var,domainPredicates) 294 [pred, domainPredicates] 295 296fixUpPredicate(predClause, domainPreds, partial, sig) == 297 -- merge the predicates in predClause and domainPreds into a 298 -- single predicate 299 [predicate, fn, :skip] := predClause 300 if first predicate = "AND" then 301 predicates := APPEND(domainPreds,rest predicate) 302 else if predicate ~= MKQ "T" 303--was->then predicates:= REVERSE [predicate, :domainPreds] 304 then predicates:= [predicate, :domainPreds] 305 else predicates := domainPreds or [predicate] 306 if #predicates > 1 then 307 pred := ["AND",:predicates] 308 [pred,:dependList]:=orderPredicateItems(pred,sig,skip) 309 else 310 pred := orderPredicateItems(first predicates,sig,skip) 311 dependList:= if pred is ['isDomain,pvar,[.]] then [pvar] else nil 312 pred := moveORsOutside pred 313 if partial then pred := ["partial", :pred] 314 [[pred, fn, :skip],:dependList] 315 316moveORsOutside p == 317 p is ['AND,:q] => 318 q := [moveORsOutside r for r in q] 319 x := or/[r for r in q | r is ['OR,:s]] => 320 moveORsOutside(['OR, :[['AND, :substitute(t, x, q)] for t in rest x]]) 321 ['AND,:q] 322 p 323 324replaceVars(x,oldvars,newvars) == 325 -- replace every identifier in oldvars with the corresponding 326 -- identifier in newvars in the expression x 327 for old in oldvars for new in newvars repeat 328 x := substitute(new,old,x) 329 x 330 331getDomainFromMm mm == 332 -- Returns the Domain (or package or category) of origin from a pattern 333 -- modemap 334 [., cond] := mm 335 if cond is ['partial, :c] then cond := c 336 condList := 337 cond is ['AND, :cl] => cl 338 cond is ['OR, ['AND, :cl],:.] => cl --all cl's should give same info 339 [cond] 340 val := 341 for condition in condList repeat 342 condition is ['isDomain, "*1", dom] => return opOf dom 343 condition is ['ofCategory, "*1", cat] and _ 344 not(member(opOf cat, ["finiteAggregate", "shallowlyMutable", _ 345 "arbitraryPrecision", "canonicalUnitNormal"]))_ 346 => return opOf cat 347 null val => 348 keyedSystemError("S2GE0016", 349 ['"getDomainFromMm",'"Can't find domain in modemap condition"]) 350 val 351 352getFirstArgTypeFromMm mm == 353 -- Returns the type of the first argument or nil 354 [pats, cond] := mm 355 [.,.,:args] := pats 356 null args => nil 357 arg1 := first args 358 if cond is ['partial, :c] then cond := c 359 condList := 360 cond is ['AND, :cl] => cl 361 cond is ['OR, ['AND, :cl],:.] => cl --all cl's should give same info 362 [cond] 363 type := nil 364 for condition in condList while not type repeat 365 if condition is ['isDomain, a1, dom] and a1=arg1 then type := dom 366 type 367 368isFreeFunctionFromMm mm == 369 -- This returns true is the modemap represents a free function, ie, 370 -- one not coming from a domain or category. 371 [., cond] := mm 372 isFreeFunctionFromMmCond cond 373 374isFreeFunctionFromMmCond cond == 375 -- This returns true is the modemap represents a free function, ie, 376 -- one not coming from a domain or category. 377 if cond is ['partial, :c] then cond := c 378 condList := 379 cond is ['AND, :cl] => cl 380 cond is ['OR, ['AND, :cl],:.] => cl --all cl's should give same info 381 [cond] 382 iff := false 383 for condition in condList while not iff repeat 384 if condition is ['isFreeFunction, :.] then iff := true 385 iff 386 387getAllModemapsFromDatabase(op,nargs) == 388 $getUnexposedOperations: local := true 389 startTimingProcess 'diskread 390 ans := getSystemModemaps(op,nargs) 391 stopTimingProcess 'diskread 392 ans 393 394getModemapsFromDatabase(op,nargs) == 395 $getUnexposedOperations: local := false 396 startTimingProcess 'diskread 397 ans := getSystemModemaps(op,nargs) 398 stopTimingProcess 'diskread 399 ans 400 401getSystemModemaps(op,nargs) == 402 mml:= GETDATABASE(op,'OPERATION) => 403 mms := NIL 404 for (x := [[.,:sig],.]) in mml repeat 405 (NUMBERP nargs) and (nargs ~= #QCDR sig) => 'iterate 406 $getUnexposedOperations or isFreeFunctionFromMm(x) or 407 isExposedConstructor(getDomainFromMm(x)) => mms := [x,:mms] 408 'iterate 409 mms 410 nil 411 412mkAlistOfExplicitCategoryOps target == 413 if target is ['add,a,:l] then 414 target:=a 415 target is ['Join,:l] => 416 "union"/[mkAlistOfExplicitCategoryOps cat for cat in l] 417 target is ['CATEGORY,.,:l] => 418 l:= flattenSignatureList ['PROGN,:l] 419 u:= 420 [[atomizeOp op,:sig] for x in l | x is ['SIGNATURE,op,sig,:.]] 421 where 422 atomizeOp op == 423 atom op => op 424 op is [a] => a 425 keyedSystemError("S2GE0016", 426 ['"mkAlistOfExplicitCategoryOps",'"bad signature"]) 427 opList:= REMDUP ASSOCLEFT u 428 [[x,:fn(x,u)] for x in opList] where 429 fn(op,u) == 430 u is [[a,:b],:c] => (a=op => [b,:fn(op,c)]; fn(op,c)) 431 isCategoryForm(target) => nil 432 keyedSystemError("S2GE0016", 433 ['"mkAlistOfExplicitCategoryOps",'"bad signature"]) 434 435flattenSignatureList(x) == 436 atom x => nil 437 x is ['SIGNATURE,:.] => [x] 438 x is ['IF,cond,b1,b2] => 439 append(flattenSignatureList b1, flattenSignatureList b2) 440 x is ['PROGN,:l] => 441 ll:= [] 442 for x in l repeat 443 x is ['SIGNATURE,:.] => ll:=cons(x,ll) 444 ll:= append(flattenSignatureList x,ll) 445 ll 446 nil 447 448mkDatabasePred [a,t] == 449 isCategoryForm(t) => ['ofCategory, a, t] 450 ['ofType,a,t] 451 452formal2Pattern x == 453 SUBLIS(pairList($FormalMapVariableList,rest $PatternVariableList),x) 454 455updateDatabase(fname,cname,systemdir?) == 456 -- for now in NRUNTIME do database update only if forced 457 not $forceDatabaseUpdate => nil 458 clearClams() 459 clearAllSlams [] 460 if constructor? cname then 461 if GET(cname, 'LOADED) then 462 clearConstructorCaches() 463 464REMOVER(lst,item) == 465 --destructively removes item from lst 466 not PAIRP lst => 467 lst=item => nil 468 lst 469 first lst=item => rest lst 470 RPLNODE(lst,REMOVER(first lst,item),REMOVER(rest lst,item)) 471 472allLASSOCs(op,alist) == 473 [value for [key,:value] in alist | key = op] 474 475--% Miscellaneous Stuff 476 477getOplistForConstructorForm (form := [op,:argl]) == 478 -- The new form is an op-Alist which has entries (<op> . signature-Alist) 479 -- where signature-Alist has entries (<signature> . item) 480 -- where item has form (<slotNumber> <condition> <kind>) 481 -- where <kind> = ELT | CONST | (XLAM..) .. 482 pairlis:= [[fv,:arg] for fv in $FormalMapVariableList for arg in argl] 483 opAlist := getOperationAlistFromLisplib op 484 [:getOplistWithUniqueSignatures(op,pairlis,signatureAlist) 485 for [op,:signatureAlist] in opAlist] 486 487getOplistWithUniqueSignatures(op,pairlis,signatureAlist) == 488 alist:= nil 489 for [sig, :[slotNumber, pred, kind]] in signatureAlist repeat 490 key := SUBLIS(pairlis, [op, sig]) 491 term := assoc(key, alist) 492 if null term then 493 alist := cons([key, pred, [kind, nil, slotNumber]], alist) 494 else 495 value := rest term 496 oldpred := first value 497 newpred := 498 oldpred = true or pred = true => true 499 oldpred = pred => oldpred 500 oldpred is ['OR, :predl] => 501 member(pred, predl) => oldpred 502 ['OR, pred, :predl] 503 ['OR, pred, oldpred] 504 RPLACA(value, newpred) 505 alist 506 507--% Exposure Group Code 508 509dropPrefix(fn) == 510 member(fn.0,[char "?",char "-",char "+"]) => SUBSTRING(fn,1,nil) 511 fn 512 513DEFPARAMETER($globalExposureHash, nil) 514 515initExposureHash() == 516 $globalExposureHash := MAKE_HASHTABLE('EQUAL) 517 for grdata in $globalExposureGroupAlist repeat 518 group := first(grdata) 519 alist := rest(grdata) 520 for pair in alist repeat 521 name := first(pair) 522 ogr := HGET($globalExposureHash, name) 523 HPUT($globalExposureHash, name, [group, :ogr]) 524 525isExposedConstructor name == 526 -- this function checks the local exposure data in the frame to 527 -- see if the given constructor is exposed. The format of 528 -- $localExposureData is a vector with 529 -- slot 0: list of groups exposed in the frame 530 -- slot 1: list of constructors explicitly exposed 531 -- slot 2: list of constructors explicitly hidden 532 -- check if it is explicitly hidden 533 MEMQ(name,'(Union Record Mapping)) => true 534 MEMQ(name,$localExposureData.2) => false 535 -- check if it is explicitly exposed 536 MEMQ(name,$localExposureData.1) => true 537 -- check if it is in an exposed group 538 found := NIL 539 if null($globalExposureHash) then 540 initExposureHash() 541 exd := HGET($globalExposureHash, name) 542 for g in $localExposureData.0 while not found repeat 543 null(g in exd) => 'iterate 544 found := true 545 found 546 547displayExposedGroups() == 548 sayKeyedMsg("S2IZ0049A",[$interpreterFrameName]) 549 if null $localExposureData.0 550 then centerAndHighlight '"there are no exposed groups" 551 else for g in $localExposureData.0 repeat 552 centerAndHighlight g 553 554displayExposedConstructors() == 555 sayKeyedMsg("S2IZ0049B",NIL) 556 if null $localExposureData.1 557 then centerAndHighlight 558 '"there are no explicitly exposed constructors" 559 else for c in $localExposureData.1 repeat 560 centerAndHighlight c 561 562displayHiddenConstructors() == 563 sayKeyedMsg("S2IZ0049C",NIL) 564 if null $localExposureData.2 565 then centerAndHighlight 566 '"there are no explicitly hidden constructors" 567 else for c in $localExposureData.2 repeat 568 centerAndHighlight c 569 570getOperationAlistFromLisplib x == 571 u := GETDATABASE(x, 'OPERATIONALIST) 572 -- u := removeZeroOneDestructively u 573 null u => u -- this can happen for Object 574 CAAR u = '_$unique => rest u 575 f := addConsDB '(NIL T ELT) 576 for [op, :sigList] in u repeat 577 for items in tails sigList repeat 578 [sig, :r] := first items 579 if r is [., :s] then 580 if s is [., :t] then 581 if t is [.] then nil 582 else RPLACD(s, QCDDR f) 583 else RPLACD(r, QCDR f) 584 else RPLACD(first items, f) 585 RPLACA(items, addConsDB first items) 586 u and markUnique u 587 588markUnique x == 589 u := first x 590 RPLACA(x, '(_$unique)) 591 RPLACD(x, [u, :rest x]) 592 rest x 593 594--======================================================================= 595-- Creation of System Sig/Pred Vectors & Hash Tables 596--======================================================================= 597 598addConsDB x == x 599