/* Little Smalltalk, version 2 Written by Tim Budd, Oregon State University, July 1987 routines used in the making of the initial object image */ # include # include "env.h" # include "memory.h" # include "names.h" # include "lex.h" # ifdef STRING # include # endif # ifdef STRINGS # include # endif # define SymbolTableSize 71 # define GlobalNameTableSize 53 # define MethodTableSize 39 # define globalNameSet(sym, value) nameTableInsert(globalNames, sym, value) /* the following classes are used repeately, so we put them in globals. */ static object ObjectClass; static object ClassClass; static object LinkClass; static object DictionaryClass; static object ArrayClass; /* we read the input a line at a time, putting lines into the following buffer. In addition, all methods must also fit into this buffer. */ # define TextBufferSize 1024 static char textBuffer[TextBufferSize]; /* nameTableInsert is used to insert a symbol into a name table. see names.c for futher information on name tables */ nameTableInsert(dict, symbol, value) object dict, symbol, value; { object table, link, newLink, nextLink, tablentry; int hash; /* first get the hash table */ table = basicAt(dict, 1); if (objectSize(table) < 3) sysError("attempt to insert into","too small name table"); else { hash = 3 * ( symbol % (objectSize(table) / 3)); tablentry = basicAt(table, hash+1); if ((tablentry == nilobj) || (tablentry == symbol)) { basicAtPut(table, hash+1, symbol); basicAtPut(table, hash+2, value); } else { newLink = allocObject(3); incr(newLink); setClass(newLink, globalSymbol("Link")); basicAtPut(newLink, 1, symbol); basicAtPut(newLink, 2, value); link = basicAt(table, hash+3); if (link == nilobj) basicAtPut(table, hash+3, newLink); else while(1) if (basicAt(link,1) == symbol) { basicAtPut(link, 2, value); break; } else if ((nextLink = basicAt(link, 3)) == nilobj) { basicAtPut(link, 3, newLink); break; } else link = nextLink; decr(newLink); } } } /* there is sort of a chicken and egg problem about building the first classes. in order to do it, you need symbols, but in order to make symbols, you need the class Symbol. the routines makeClass and buildInitialNameTable attempt to get carefully get around this initialization problem */ static object makeClass(name) char *name; { object theClass, theSymbol; /* this can only be called once newSymbol works properly */ theClass = allocObject(classSize); theSymbol = newSymbol(name); basicAtPut(theClass, nameInClass, theSymbol); globalNameSet(theSymbol, theClass); setClass(theClass, ClassClass); return(theClass); } buildInitialNameTables() { object symbolString, classString; object globalHashTable; int hash; char *p; /* build the table that contains all symbols */ symbols = allocObject(2 * SymbolTableSize); incr(symbols); /* build the table (a dictionary) that contains all global names */ globalNames = allocObject(1); globalHashTable = allocObject(3 * GlobalNameTableSize); incr(globalNames); basicAtPut(globalNames, 1, globalHashTable); /* next create class Symbol, so we can call newSymbol */ /* notice newSymbol uses the global variable symbolclass */ symbolString = allocSymbol("Symbol"); symbolclass = allocObject(classSize); setClass(symbolString, symbolclass); basicAtPut(symbolclass, nameInClass, symbolString); /* we recreate the hash computation used by newSymbol */ hash = 0; for (p = "Symbol"; *p; p++) hash += *p; if (hash < 0) hash = - hash; hash %= (objectSize(symbols) / 2); basicAtPut(symbols, 2*hash + 1, symbolString); globalNameSet(symbolString, symbolclass); /* now the routine newSymbol should work properly */ /* now go on to make class Class so we can call makeClass */ ClassClass = allocObject(classSize); classString = newSymbol("Class"); basicAtPut(ClassClass, nameInClass, classString); globalNameSet(classString, ClassClass); setClass(ClassClass, ClassClass); setClass(symbolclass, ClassClass); /* now create a few other important classes */ ObjectClass = makeClass("Object"); LinkClass = makeClass("Link"); setClass(nilobj, makeClass("UndefinedObject")); DictionaryClass = makeClass("Dictionary"); ArrayClass = makeClass("Array"); setClass(symbols, DictionaryClass); setClass(globalNames, DictionaryClass); setClass(globalHashTable, ArrayClass); } /* findClass gets a class object, either by finding it already or making it in addition, it makes sure it has a size, by setting the size to zero if it is nil. */ static object findClass(name) char *name; { object newobj; newobj = globalSymbol(name); if (newobj == nilobj) newobj = makeClass(name); if (basicAt(newobj, sizeInClass) == nilobj) basicAtPut(newobj, sizeInClass, newInteger(0)); return(newobj); } /* readDeclaration reads a declaration of a class */ static readDeclaration() { object classObj, super, vars; int i, size, instanceTop; object instanceVariables[15]; if (nextToken() != nameconst) sysError("bad file format","no name in declaration"); classObj = findClass(tokenString); size = 0; if (nextToken() == nameconst) { /* read superclass name */ super = findClass(tokenString); basicAtPut(classObj, superClassInClass, super); size = intValue(basicAt(super, sizeInClass)); ignore nextToken(); } if (token == nameconst) { /* read instance var names */ instanceTop = 0; while (token == nameconst) { instanceVariables[instanceTop++] = newSymbol(tokenString); size++; ignore nextToken(); } vars = newArray(instanceTop); for (i = 0; i < instanceTop; i++) basicAtPut(vars, i+1, instanceVariables[i]); basicAtPut(classObj, variablesInClass, vars); } basicAtPut(classObj, sizeInClass, newInteger(size)); } /* readInstance - read an instance directive */ static readInstance() { object classObj, newObj; int size; if (nextToken() != nameconst) sysError("no name","following instance command"); classObj = globalSymbol(tokenString); if (nextToken() != nameconst) sysError("no instance name","in instance command"); /* now make a new instance of the class - note that we can't do any initialization */ size = intValue(basicAt(classObj, sizeInClass)); newObj = allocObject(size); setClass(newObj, classObj); globalNameSet(newSymbol(tokenString), newObj); } /* readClass reads a class method description */ static readClass(fd, printit) FILE *fd; boolean printit; { object classObj, methTable, theMethod, selector; # define LINEBUFFERSIZE 512 object methDict; char *eoftest, lineBuffer[LINEBUFFERSIZE]; /* if we haven't done it already, read symbols now */ if (trueobj == nilobj) initCommonSymbols(); if (nextToken() != nameconst) sysError("missing name","following Class keyword"); classObj = findClass(tokenString); setInstanceVariables(classObj); if (printit) ignore fprintf(stderr,"class %s\n", charPtr(basicAt(classObj, nameInClass))); /* find or create a methods table */ methTable = basicAt(classObj, methodsInClass); if (methTable == nilobj) { methTable = allocObject(1); basicAtPut(classObj, methodsInClass, methTable); setClass(methTable, globalSymbol("Dictionary")); methDict = allocObject(MethodTableSize); basicAtPut(methTable, 1, methDict); setClass(methDict, globalSymbol("Array")); } /* now go read the methods */ do { textBuffer[0] = '\0'; while((eoftest = fgets(lineBuffer, LINEBUFFERSIZE, fd)) != NULL) { if ((lineBuffer[0] == '|') || (lineBuffer[0] == ']')) break; ignore strcat(textBuffer, lineBuffer); } if (eoftest == NULL) { sysError("unexpected end of file","while reading method"); break; } /* now we have a method */ theMethod = allocObject(methodSize); setClass(theMethod, globalSymbol("Method")); if (parse(theMethod, textBuffer)) { selector = basicAt(theMethod, messageInMethod); if (printit) ignore fprintf(stderr,"method %s\n", charPtr(selector)); nameTableInsert(methTable, selector, theMethod); } else { /* get rid of unwanted method */ incr(theMethod); decr(theMethod); ignore fprintf(stderr,"push return to continue\n"); ignore gets(textBuffer); } } while (lineBuffer[0] != ']'); } /* readFile reads a class descriptions file */ readFile(fd, printit) FILE *fd; boolean printit; { while(fgets(textBuffer, TextBufferSize, fd) != NULL) { lexinit(textBuffer); if (token == inputend) ; /* do nothing, get next line */ else if ((token == binary) && streq(tokenString, "*")) ; /* do nothing, its a comment */ else if ((token == nameconst) && streq(tokenString, "Declare")) readDeclaration(); else if ((token == nameconst) && streq(tokenString,"Instance")) readInstance(); else if ((token == nameconst) && streq(tokenString,"Class")) readClass(fd, printit); else ignore fprintf(stderr,"unknown line %s\n", textBuffer); } } o create global variables merely by assiging a value to them at