-- file PGSControl.mesa -- last modified by Satterthwaite, June 23, 1982 10:22 am DIRECTORY CharIO: TYPE USING [GetChar, PutChar, PutString], CommandUtil: TYPE USING [ PairList, CopyString, FreeString, KeyValue, ListLength, SetExtension], Inline: TYPE USING [BITOR, DIVMOD], Environment: TYPE USING [bytesPerWord], File: TYPE USING [ Capability, nullCapability, Permissions, delete, grow, read, shrink, write, LimitPermissions], FileStream: TYPE USING [ FileByteIndex, Create, EndOf, GetIndex, GetLeaderProperties, SetIndex], OSMiscOps: TYPE USING [ FileError, FindFile, GenerateUniqueId--, ImageId--, RenameFile], PGS1: TYPE USING [Parse], PGSConDefs: TYPE USING [ ControlZ, AcquireTable, bitstrsize, cpw, pageSize, FixupBcdHeader, Format, LALRGen, OutModule, PrintGrammar, ReleaseTable, TabGen, WriteBcdHeader], PGSOps: TYPE USING [PGSPhase], PGSParseData: TYPE, PGSTypes: TYPE USING [ Aliases, LongDes, LongPointer, Options, ProdInfo, RhsChar, SymTab, SymInfo, TokenInfo], Segments: TYPE USING [ModifyFile], Spaces: TYPE USING [FreeWords, Words], Stream: TYPE USING [Handle, Delete, GetWord, PutBlock, PutWord], Strings: TYPE USING [ String, SubStringDescriptor, AppendChar, AppendString, EqualSubStrings, EquivalentSubStrings], Time: TYPE USING [Packed, Append, Current, Unpack], TimeStamp: TYPE USING [Stamp]; PGSControl: PROGRAM IMPORTS CharIO, CommandUtil, File, FileStream, Inline, OSMiscOps, PGS1, PGSConDefs, PGSParseData, Segments, Spaces, Stream, Strings, Time EXPORTS PGSConDefs, PGSOps, PGS1 = { eofile, totalTokens, numprod, numrules, nextAlias: PUBLIC CARDINAL; warningsLogged: PUBLIC BOOL; flags: PUBLIC ARRAY PGSTypes.Options OF BOOL; symtab: PUBLIC PGSTypes.SymTab; syminfo: PUBLIC PGSTypes.SymInfo; aliases: PUBLIC PGSTypes.Aliases; tokeninfo: PUBLIC PGSTypes.TokenInfo; prodinfo: PUBLIC PGSTypes.ProdInfo; rhschar: PUBLIC PGSTypes.RhsChar; slim, tentries, ntentries: PUBLIC CARDINAL; bitstrsize: PUBLIC CARDINAL; PGSFail: PUBLIC ERROR = CODE; outStream: Stream.Handle; outeol: PUBLIC PROC [n: CARDINAL] = { THROUGH [1..n] DO CharIO.PutChar[outStream,'\n] ENDLOOP}; outchar: PUBLIC PROC [c: CHAR, n: INTEGER]= { THROUGH [1..n] DO CharIO.PutChar[outStream,c] ENDLOOP}; outstring: PUBLIC PROC [string: Strings.String] = {CharIO.PutString[outStream,string]}; outtab: PUBLIC PROC = {CharIO.PutChar[outStream,'\t]}; outnum: PUBLIC PROC [val: INTEGER, cols: CARDINAL, signChar: CHAR←'-] = { i: CARDINAL; power, digits: CARDINAL ← 1; num: CARDINAL ← ABS[val]; sign: CARDINAL = IF val<0 THEN 1 ELSE 0; WHILE (i←power*10)<=num DO power ← i; digits ← digits+1 ENDLOOP; outchar[' , INTEGER[cols-digits-sign]]; IF sign#0 THEN CharIO.PutChar[outStream,signChar]; UNTIL power < 1 DO [i,num] ← Inline.DIVMOD[num,power]; CharIO.PutChar[outStream,i+'0]; power ← power/10; ENDLOOP}; startTime: Time.Packed; outtime: PUBLIC PROC = { time: STRING = [20]; Time.Append[time, Time.Unpack[startTime]]; time.length ← time.length-3; CharIO.PutString[outStream,time]}; -- storage allocation for PGSscan, PGSlalr, PGStab LongDes: TYPE = PGSTypes.LongDes; LongPointer: TYPE = PGSTypes.LongPointer; MakeArray: PUBLIC PROC [length, width: CARDINAL] RETURNS [LongDes] = { n: CARDINAL = length*width; new: LongPointer = Spaces.Words[n]; FOR i: CARDINAL IN [0..n) DO (new+i)↑ ← 0 ENDLOOP; RETURN [DESCRIPTOR[new, length]]}; Expand: PUBLIC PROC [des: LongDes, width, ext: CARDINAL] RETURNS [LongDes] = { new, old: LongPointer; i: CARDINAL; new ← Spaces.Words[(LENGTH[des]+ext)*width]; old ← BASE[des]; FOR i IN [0..LENGTH[des]*width) DO (new+i)↑ ← (old+i)↑ ENDLOOP; FOR i IN [LENGTH[des]*width..(LENGTH[des]+ext)*width) DO (new+i)↑ ← 0 ENDLOOP; IF old # NIL THEN Spaces.FreeWords[old]; RETURN [DESCRIPTOR[new, LENGTH[des]+ext]]}; FreeArray: PUBLIC PROC [des: LongDes] = { base: LongPointer ← BASE[des]; IF base # NIL THEN Spaces.FreeWords[base]}; orCount: PUBLIC CARDINAL; OrBits: PUBLIC PROC [source, sink: LongPointer] = { FOR i: CARDINAL IN [0..PGSConDefs.bitstrsize) DO (sink+i)↑ ← Inline.BITOR[(sink+i)↑,(source+i)↑] ENDLOOP; orCount ← orCount+1}; -- streams and files writeAccess: File.Permissions = File.write+File.grow+File.shrink+File.delete; sourcestr, outstr, errstr: Stream.Handle ← NIL; inputFile, tempFile: File.Capability; sourceName: PUBLIC Strings.String ← NIL; sourceVersion: PUBLIC TimeStamp.Stamp; objectName: Strings.String ← NIL; objectVersion: PUBLIC TimeStamp.Stamp; defsName: Strings.String ← NIL; gfName: Strings.String ← NIL; CreateTime: PROC [s: Stream.Handle] RETURNS [time: Time.Packed] = { RETURN [FileStream.GetLeaderProperties[s].create]}; DefaultFileName: PROC [name, defaultExtension: Strings.String] = { FOR i: CARDINAL IN [0..name.length) DO IF name[i] = '. THEN RETURN ENDLOOP; Strings.AppendString[name, defaultExtension]}; getstream: PROC [dotstring: Strings.String] RETURNS [Stream.Handle] = { fileName: STRING ← [40]; fileName.length ← 0; Strings.AppendString[fileName, rootName]; Strings.AppendString[fileName, dotstring]; RETURN [FileStream.Create[OSMiscOps.FindFile[fileName, write]]]}; seterrstream: PUBLIC PROC = { IF errstr = NIL THEN { outStream ← errstr ← getstream[".errlog"L]; outstring["Mesa PGS of "L]; outtime[]; outstring[" -- "L]; outstring[rootName]; outstring[".errlog"L]; outeol[2]} ELSE outStream ← errstr}; closeerrstream: PROC = { IF errstr # NIL THEN {Stream.Delete[errstr]; errstr ← NIL}}; setoutstream: PUBLIC PROC [dotstring: Strings.String] = { outStream ← outstr ← getstream[dotstring]}; resetoutstream: PUBLIC PROC = {outStream ← outstr}; closeoutstream: PUBLIC PROC = { IF outstr # NIL THEN {Stream.Delete[outstr]; outstr ← NIL}}; cleanupstreams: PUBLIC PROC = {NULL}; -- used for checkout openwordstream: PUBLIC PROC [scratch: BOOL] = { tempFile ← OSMiscOps.FindFile[objectName, both]; outstr ← FileStream.Create[tempFile.LimitPermissions[writeAccess]]}; inword: PUBLIC PROC RETURNS [CARDINAL] = {RETURN[outstr.GetWord[]]}; closewordstream: PUBLIC PROC = { closeoutstream[]; tempFile ← File.nullCapability}; outword: PUBLIC PROC [n: CARDINAL] = {outstr.PutWord[n]}; outblock: PUBLIC PROC [address: LongPointer, words: CARDINAL] = { outstr.PutBlock[[address, 0, words*Environment.bytesPerWord]]}; inchar: PUBLIC PROC RETURNS [c: CHAR, end: BOOL] = { IF (end ← FileStream.EndOf[sourcestr]) THEN c ← '\000 ELSE c ← CharIO.GetChar[sourcestr]; RETURN}; LocateIndex: PUBLIC PROC [index: CARDINAL] RETURNS [base: CARDINAL] = { OPEN PGSConDefs; page: CARDINAL; page ← index/(pageSize*cpw); base ← page*(pageSize*cpw); FileStream.SetIndex[sourcestr, sourceOrigin+index]}; StreamIndex: TYPE = FileStream.FileByteIndex; PrintTextLine: PROC [origin: StreamIndex] RETURNS [start: StreamIndex] = { lineIndex: StreamIndex; char: CHAR; n: [1..100]; start ← lineIndex ← origin; FOR n IN [1..100] UNTIL lineIndex = 0 DO lineIndex ← lineIndex - 1; FileStream.SetIndex[sourcestr, lineIndex]; IF CharIO.GetChar[sourcestr] = '\n THEN EXIT; start ← lineIndex; ENDLOOP; FileStream.SetIndex[sourcestr, start]; FOR n IN [1..100] UNTIL FileStream.EndOf[sourcestr] DO char ← CharIO.GetChar[sourcestr]; SELECT char FROM '\n, PGSConDefs.ControlZ => EXIT; ENDCASE => outchar[char,1]; ENDLOOP; outeol[1]; RETURN}; sourceOrigin: StreamIndex; ErrorContext: PUBLIC PROC [message: STRING, tokenIndex: CARDINAL] = { saveIndex: StreamIndex = FileStream.GetIndex[sourcestr]; origin: StreamIndex = sourceOrigin + tokenIndex; char: CHAR; seterrstream[]; FileStream.SetIndex[sourcestr, PrintTextLine[origin]]; UNTIL FileStream.GetIndex[sourcestr] = origin OR FileStream.EndOf[sourcestr] DO char ← CharIO.GetChar[sourcestr]; outchar[IF char = '\n THEN '\n ELSE ' ,1]; ENDLOOP; outstring["↑ ["L]; outnum[tokenIndex,1]; outchar['],1]; outeol[1]; outstring[message]; FileStream.SetIndex[sourcestr, saveIndex]}; -- processing options rootName: Strings.String ← NIL; SetRoot: PROC [s: Strings.String] = { root: STRING ← [40]; FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '. THEN EXIT; Strings.AppendChar[root, s[i]] ENDLOOP; rootName ← CommandUtil.CopyString[root]}; SetFileName: PROC [fileName, default, extension: Strings.String] RETURNS [Strings.String] = { root: Strings.String = IF fileName = NIL THEN CommandUtil.CopyString[default, 2+extension.length] ELSE fileName; RETURN [CommandUtil.SetExtension[root, extension]]}; TestExtension: PROC [fileName, extension: Strings.String] RETURNS [BOOL] = { t: STRING ← [40]; i: CARDINAL ← 0; ext: Strings.SubStringDescriptor ← [extension, 0, extension.length]; d: Strings.SubStringDescriptor; UNTIL i >= fileName.length OR fileName[i] = '. DO i ← i+1 ENDLOOP; i ← i+1; UNTIL i >= fileName.length OR fileName[i] = '. DO Strings.AppendChar[t, fileName[i]]; i ← i+1 ENDLOOP; d ← [t, 0, t.length]; RETURN [Strings.EquivalentSubStrings[@d, @ext]]}; KeyVal: PROC [list: CommandUtil.PairList, key: Strings.String, delete: BOOL ← TRUE] RETURNS [Strings.String] = { s: Strings.SubStringDescriptor ← [base: key, offset: 0, length: key.length]; RETURN [CommandUtil.KeyValue[@s, list, delete]]}; pgsVersion: PUBLIC TimeStamp.Stamp ← [net: 'c-0c, host: 'c-0c, time: 7*200000b+11]; -- * * * * * * HERE IT BEGINS * * * * * * NoSource: PUBLIC ERROR = CODE; LockedSource: PUBLIC ERROR = CODE; BadSemantics: PUBLIC ERROR = CODE; Generate: PUBLIC PROC [ source: Strings.String, args, results: CommandUtil.PairList, switches: Strings.String, startPhase: PROC [PGSOps.PGSPhase] RETURNS [BOOL], princOps: BOOL] RETURNS [success, warnings: BOOL] = { alto: BOOL ← ~princOps; long: BOOL← princOps; printGrammar: BOOL ← TRUE; bcd: BOOL ← FALSE; scratchExists: BOOL ← FALSE; tableBase: LONG POINTER ← NIL; typeId: STRING = [40]; tableId: STRING = [40]; exportId: STRING = [40]; sourceName ← CommandUtil.CopyString[source, 2+("mesa"L).length]; objectName ← gfName ← NIL; -- collect output specifications BEGIN nR: CARDINAL ← CommandUtil.ListLength[results]; IF (defsName ← KeyVal[results, "defs"L]) # NIL THEN nR ← nR - 1; SELECT TRUE FROM (objectName ← KeyVal[results, "bcd"L]) # NIL => {bcd ← TRUE; nR ← nR - 1}; (objectName ← KeyVal[results, "binary"L]) # NIL => {bcd ← FALSE; nR ← nR - 1}; ENDCASE; IF (gfName ← KeyVal[results, "grammar"L]) # NIL THEN nR ← nR - 1; IF nR # 0 THEN GO TO badSemantics; END; SetRoot[IF objectName # NIL THEN objectName ELSE sourceName]; IF switches # NIL THEN { sense: BOOL ← TRUE; FOR i: CARDINAL IN [0 .. switches.length) DO SELECT switches[i] FROM '-, '~ => sense ← ~sense; 'a, 'A => {alto ← sense; sense ← TRUE}; 'l, 'L => {long ← sense; sense ← TRUE}; 'g, 'G => {printGrammar ← sense; sense ← TRUE}; ENDCASE; ENDLOOP}; startTime ← Time.Current[]; warningsLogged ← warnings ← FALSE; sourceName ← CommandUtil.SetExtension[sourceName, "mesa"L]; IF sourceName[sourceName.length-1] = '. THEN sourceName.length ← sourceName.length-1; IF TestExtension[sourceName, "mesa"L] THEN { t: STRING ← [40]; -- String vs. STRING resolution copyName: Strings.String; sourceFile: File.Capability; [] ← startPhase[format]; Strings.AppendString[t, sourceName]; IF ~Segments.ModifyFile[t] THEN GO TO lockedSource; sourceFile ← OSMiscOps.FindFile[sourceName, read ! OSMiscOps.FileError => {GO TO noSource}]; copyName ← CommandUtil.CopyString[sourceName, 1]; Strings.AppendChar[copyName, '$]; OSMiscOps.RenameFile[newName: copyName, oldName: sourceName]; copyName ← CommandUtil.FreeString[copyName]; sourcestr ← FileStream.Create[sourceFile.LimitPermissions[File.read]]; tempFile ← OSMiscOps.FindFile[sourceName, both]; outstr ← FileStream.Create[tempFile.LimitPermissions[writeAccess]]; outStream ← outstr; tableId.length ← typeId.length ← exportId.length ← 0; PGSConDefs.Format[tableId, typeId, exportId ! PGSFail => {GOTO formatFailed}]; -- input from sourceName$ (errstr), modified input to sourceName (outstr), -- sets up data for PrintGrammar sourceVersion ← [0, 0, CreateTime[outstr]]; closeoutstream[]; Stream.Delete[sourcestr]; sourcestr ← NIL; tempFile ← sourceFile ← File.nullCapability; -- output grammar to summary file (or scratch) gfName ← IF printGrammar THEN SetFileName[gfName, IF tableId.length # 0 THEN tableId ELSE rootName, "grammar"L] ELSE CommandUtil.CopyString["pgs.scratch$"L]; inputFile ← OSMiscOps.FindFile[gfName, both]; gfName ← CommandUtil.FreeString[gfName]; outstr ← FileStream.Create[inputFile.LimitPermissions[writeAccess]]; outStream ← outstr; PGSConDefs.PrintGrammar[]; closeoutstream[]; IF ~printGrammar THEN scratchExists ← TRUE; -- connect pgs.scratch to input stream and fix sourceNames sourcestr ← FileStream.Create[inputFile.LimitPermissions[File.read]]; -- derive missing type id (compatibility feature) IF typeId.length = 0 AND defsName # NIL THEN FOR i: CARDINAL IN [0..defsName.length) DO IF defsName[i] = '. THEN EXIT; Strings.AppendChar[typeId, defsName[i]]; ENDLOOP; IF objectName = NIL THEN { bcd ← TRUE; IF tableId.length # 0 THEN objectName ← CommandUtil.CopyString[tableId, 2+("bcd"L).length] ELSE { objectName ← CommandUtil.CopyString[rootName, ("PGSTable"L).length]; Strings.AppendString[objectName, "PGSTable"L]}} EXITS formatFailed => { closeoutstream[]; closeerrstream[]; seterrstream[]; outeol[1]; outstring["Directives incorrect or out of sequence"L]; outeol[1]; tempFile ← File.nullCapability; GO TO fail}} ELSE { sourcestr ← FileStream.Create[ OSMiscOps.FindFile[sourceName, read ! OSMiscOps.FileError => {GO TO noSource}]]; sourceVersion ← [0, 0, CreateTime[sourcestr]]; IF objectName = NIL THEN objectName ← CommandUtil.CopyString[rootName, 2+("binary"L).length]; -- derive type name Strings.AppendString[typeId, rootName]; Strings.AppendString[typeId, "PGSTableType"L]}; IF defsName = NIL THEN { IF typeId.length # 0 THEN defsName ← CommandUtil.CopyString[typeId, 2+("mesa"L).length] ELSE { defsName ← CommandUtil.CopyString[rootName, ("PGSTableType"L).length]; Strings.AppendString[defsName,"PGSTableType"L]}}; defsName ← CommandUtil.SetExtension[defsName, "mesa"L]; objectName ← CommandUtil.SetExtension[objectName, IF bcd THEN "bcd"L ELSE "binary"L]; outstr ← errstr ← NIL; sourceOrigin ← FileStream.GetIndex[sourcestr]; -- load table and call first pass here [] ← startPhase[lalr]; objectVersion ← OSMiscOps.GenerateUniqueId[]; tableBase ← PGSConDefs.AcquireTable[]; success ← PGS1.Parse[tableBase].nErrors = 0; PGSConDefs.ReleaseTable[]; tableBase ← NIL; Stream.Delete[sourcestr]; closeoutstream[]; IF scratchExists THEN inputFile ← File.nullCapability; -- now if no errors generate the tables then package them on request IF success AND (flags[lists] OR flags[printLALR] OR flags[printLR]) THEN { success ← PGSConDefs.LALRGen[ ! PGSFail => {success ← FALSE; CONTINUE}]; IF success AND flags[lists] THEN { InitBcd: PROC = { self: Strings.SubStringDescriptor ← ["SELF"L, 0, ("SELF"L).length]; export: Strings.SubStringDescriptor ← [exportId, 0, exportId.length]; PGSConDefs.WriteBcdHeader[ outstr, tableId, objectName, IF Strings.EqualSubStrings[@export,@self] THEN NIL ELSE exportId, KeyVal[args, exportId, FALSE], alto]}; closeoutstream[]; -- flush output from LALRGen outstr ← FileStream.Create[tempFile.LimitPermissions[File.read]]; -- for reinput success ← IF exportId.length # 0 THEN PGSConDefs.TabGen[prefix:InitBcd, suffix:PGSConDefs.FixupBcdHeader] ELSE PGSConDefs.TabGen[NIL, NIL]; IF ~success THEN closewordstream[] ELSE { closeoutstream[]; -- flush tabgen output outstr ← FileStream.Create[OSMiscOps.FindFile[defsName, write]]; outStream ← outstr; PGSConDefs.OutModule[typeId, defsName, long]; closeoutstream[]}}}; closeerrstream[]; warnings ← warningsLogged; rootName ← CommandUtil.FreeString[rootName]; sourceName ← CommandUtil.FreeString[sourceName]; EXITS badSemantics => ERROR BadSemantics; noSource => ERROR NoSource; lockedSource => ERROR LockedSource; fail => { rootName ← CommandUtil.FreeString[rootName]; sourceName ← CommandUtil.FreeString[sourceName]; closeerrstream[]; success ← FALSE}}; }.