LupineManagerImpl.mesa.
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by BZM on 12-May-82 19:22:17.
Last edited by Andrew Birrell on October 25, 1983 10:37 am
Last Edited by: Swinehart, February 8, 1985 9:06:22 am PST
Bob Hagmann February 12, 1985 12:53:46 pm PST
DIRECTORY
Ascii USING [BS, CR, FF, SP, TAB],
BasicTime USING [earliestGMT, GMT, Now, Period, Unpack, Update],
Convert USING [RopeFromInt],
FileStream USING [OpenFileFromStream],
FS USING[ Delete, Error, GetInfo, nullOpenFile, Open, OpenFile, SetByteCountAndCreatedTime, StreamOpen ],
IO USING [Close, CreateStreamProcs, CreateStream, Flush, PutChar, PutF, PutFR, PutRope, STREAM, Value],
Loader USING [BCDBuildTime],
LupineMakeRpcModules USING [
MakeRpcControlModule, MakeRpcClientModule,
MakeRpcClientBinderModule, MakeRpcServerModule ],
LupineManager USING [],
LupineManagerPrivate USING [
Abort, Error,
ErrorCode, ErrorHandlerProc, ErrorType,
ExplanationProc, IsNull,
Language,
ModuleTypes, Nest, Options, ParamPassingMethod,
SHORT, String, StringNIL],
LupineMarshal USING [ParameterProtocolVersions],
LupineSymbolTable USING [
CloseInterface, GetInterfaceInfo, GMT, InterfaceInfo,
OpenError, OpenInterface, VersionStampString, VersionStamp ],
Rope USING [Cat, Find, Length, ROPE, Substr];
LupineManagerImpl: MONITOR
IMPORTS
BasicTime, Convert, FileStream, FS, IO, Loader,
LupineMakeRpcModules, LupineManagerPrivate,
Marshal: LupineMarshal, ST: LupineSymbolTable,
Rope
EXPORTS LupineManager, LupineManagerPrivate =
BEGIN OPEN LupineManagerPrivate;
LastCodeFileGMT: BasicTime.GMT ← BasicTime.earliestGMT;
Top-level translator routine.
TranslateRemoteInterface: PUBLIC ENTRY PROCEDURE [
interfaceBcdFilename: String,
interfaceBcdCapability: FS.OpenFile ← FS.nullOpenFile,
errorHandler: ErrorHandlerProc,
options: Options,
desiredLineLength: LONG INTEGER ] =
BEGIN ENABLE BEGIN
UNWIND => NULL;
AbortTranslation => GOTO AbortTranslation;
ST.OpenError => -- This can come from ST.Open or MakeRpcXXX.
Abort[
code: (SELECT why FROM
badFileFormat => BadBcdFileFormat,
badFileName => NoSuchFile,
badFileVersion => BadBcdFileVersion,
notInterfaceModule => NotInterfaceModule,
ENDCASE => ERROR),
problemText: fileOrModuleName];
END; -- ENABLE.
EstablishErrorHandler[errorHandler];
SetCodeLineLength[desiredLineLength];
GetInterface[interfaceBcdFilename, interfaceBcdCapability];
Can raise ST.OpenError.
BEGIN ENABLE UNWIND => {
ST.CloseInterface;
ClearErrorHandler };
interfaceName: String;
contents: ST.InterfaceInfo;
[contents: contents, moduleName: interfaceName] ← ST.GetInterfaceInfo[];
MakeModuleNames[interfaceName, options.targetLanguage];
BEGIN ENABLE UNWIND => FreeModuleNames[];
DeleteExistingCodeFiles;
IF contents.variables THEN Error[InterfaceVariables, interfaceBcdFilename];
IF contents.transfers[Port] OR contents.transfers[Process]
OR contents.transfers[Program] OR contents.transfers[Other]
THEN Error[UnsupportedTransfers, interfaceBcdFilename];
BEGIN OPEN LupineMakeRpcModules;
MakeRpcControlModule[options ! AbortTranslation => CONTINUE];
MakeRpcClientModule[options ! AbortTranslation => CONTINUE];
MakeRpcClientBinderModule[options ! AbortTranslation => CONTINUE];
MakeRpcServerModule[options ! AbortTranslation => CONTINUE];
END;
END; -- ENABLE UNWIND => FreeModuleNames.
FreeModuleNames[];
END; -- ENABLE UNWIND => ST.CloseInterface.
ST.CloseInterface;
ClearErrorHandler;
EXITS
AbortTranslation => NULL; -- The GOTO's UNWIND will cleanup.
END; -- TranslateRemoteInterface.
GetInterface: PROCEDURE [
interfaceFilename: String,
interfaceCapability: FS.OpenFile ] =
BEGIN
IF interfaceCapability = FS.nullOpenFile
THEN interfaceCapability ← FS.Open[AppendBcd[interfaceFilename]
! FS.Error => GOTO noFile ];
ST.OpenInterface[interfaceFilename, interfaceCapability];
EXITS
noFile => Abort[code: NoSuchFile, problemText: interfaceFilename];
END;
AppendBcd: PROCEDURE [s: String] RETURNS[new: String] =
INLINE BEGIN
IF Rope.Find[s, "."] >= 0 THEN new ← s ELSE new ← s.Cat[".bcd"]
END;
Client-level error handling routines.
clientErrorHandler: ErrorHandlerProc ← NIL;
EstablishErrorHandler: PROCEDURE [handler: ErrorHandlerProc] =
INLINE {clientErrorHandler ← handler};
ClearErrorHandler: PROCEDURE = INLINE {clientErrorHandler ← NIL};
AbortTranslation: PRIVATE ERROR = CODE;
Raised by ReportError and caught by TranslateRemoteInterface.
ReportError: PUBLIC PROCEDURE [
type: ErrorType, code: ErrorCode, problemText: String ] =
BEGIN
Report: ExplanationProc =
BEGIN
clientSaysAbort: BOOLEAN = clientErrorHandler[
type: type, code: code,
codeExplanation: explanation,
outputFileName: GetCodeFileName[],
outputFileCharPosition: GetCodeStreamIndex[],
problemCausingText: problemText ].abortTranslation;
IF type=abort OR clientSaysAbort THEN ERROR AbortTranslation;
END; -- Explainer.
GiveExplanation[code: code, explainer: Report];
END;
GiveExplanation: PUBLIC PROCEDURE [
code: ErrorCode, explainer: ExplanationProc ] =
BEGIN
Explanation: PACKED ARRAY ErrorCode OF --explanation:-- String = [
AnonymousIdentifier:
"Unnamed item (probably record component) encountered; cannot marshal this anonymous item.",
BadBcdFileFormat:
"Module or file has invalid BCD or symbols format.",
BadBcdFileVersion:
"So-called BCD file has an incorrect version stamp.",
ComputedVariant:
"Address-containing computed or overlaid variant record encountered; cannot marshal these records.",
ComputedSequence:
"Computed sequence encountered; cannot marshal these sequences.",
EmbeddedRESULT:
"VAR or RESULT parameter has embedded addresses; cannot marshal these parameters.",
EmptyArray:
"Empty (hint for dynamic?) array encountered; use explicit descriptors and sequences instead.",
HandleREF:
"REF-containing type being passed as a handle; this is extremely dangerous for the collector.",
ImproperPassingMethod:
"Parameter uses call by VAR, VALUE, RESULT, or HANDLE improperly.",
ImproperReadonlyRESULT:
"Readonly parameter cannot be called by VAR or RESULT.",
ImproperRESULTResult:
"Result parameter called by VAR or RESULT; only arguments can be called by VAR and RESULT.",
InterfaceVariables:
"Interface contains variables; interface variables are not supported.",
InvalidHandle:
"Ref or pointer to unspecified, completely opaque, or ANY encountered; it must be passed as a HANDLE.",
MdsAllocation:
"Argument or result requires allocating short POINTER storage (in the MDS).",
NoSuchFile:
"Could not find required file or module.",
NotInterfaceModule:
"Specified file is not a DEFINITIONS module; PROGRAM module translation is not supported.",
ProbablePointerRecursion:
"Probable list, tree, or graph detected; can only marshal explicit LIST OF types.",
TransferParameter:
"Cannot YET marshal transfer (e.g., procedure) parameter.",
SequenceInsideVariant:
"An arm of a variant record contains a sequence; this marshaling is not implemented.",
ShortInterMdsPointers:
"Parameter of interMDS call contains short pointers; use long pointers instead.",
UnimplementedMarshaling:
"Marshaling this type is either impossible or unimplemented.",
Unknown:
"<No error description available.>",
UnsupportedTransfers:
"Interface contains PROCESSES, PROGRAMS, or PORTS; only procedures, signals, and errors are supported." ];
explainer[Explanation[code]];
END;
Routines that construct the string names of important modules.
moduleNames: ARRAY ModuleTypes OF String ← ALL[StringNIL];
ModuleName: PUBLIC PROC [module: ModuleTypes] RETURNS [--name:-- String] =
BEGIN RETURN[moduleNames[module]] END;
MakeModuleNames: PROCEDURE [interfaceName: String, language: Language] =
BEGIN
RpcInterfaceName: PACKED ARRAY Language OF String = [
Cedar: "RPC", Mesa: "MesaRPC" ];
RpcLupineInterfaceName: PACKED ARRAY Language OF String = [
Cedar: "RPCLupine", Mesa: "MesaRPCLupine" ];
MakeName: PROC [root: String, suffix: String←StringNIL]
RETURNS[rootSuffix: String] =
BEGIN
RETURN[root.Cat[suffix]]
END; -- MakeName.
moduleNames[interface] ← MakeName[interfaceName];
moduleNames[openedInterface] ← MakeName[StringNIL];
Global OPEN 'interfaceName'. if openedInterface=NIL; named otherwise.
moduleNames[control] ← MakeName[interfaceName, "RpcControl"];
moduleNames[client] ← MakeName[interfaceName, "RpcClientImpl"];
moduleNames[clientBinder] ← MakeName[interfaceName, "RpcBinderImpl"];
moduleNames[server] ← MakeName[interfaceName, "RpcServerImpl"];
moduleNames[rpcPublic] ← MakeName[RpcInterfaceName[language]];
OPEN RpcPublic: RPC (or MesaRPC).
moduleNames[rpcPrivate] ← MakeName[RpcLupineInterfaceName[language]];
OPEN RpcPrivate: RPCLupine (or MesaRPCLupine).
moduleNames[lupineRuntime] ← MakeName["LupineRuntime"];
OPEN Lupine: LupineRuntime.
END;
FreeModuleNames: PROCEDURE [] =
BEGIN
FOR module: ModuleTypes IN ModuleTypes DO
moduleNames[module] ← NIL;
ENDLOOP;
END;
Output file routines (via IO) for the Mesa code that Lupine generates.
codeFilename: String ← NIL;
codeStream: ARRAY [1..9] OF IO.STREAMALL[NIL];
codeStreamIndex: ARRAY [1..9] OF LONG INTEGERALL[0];
OutputFileCount: INT ← 1;
CurrentOutputFile: INT ← 1;
OpenCodeFile: PUBLIC PROCEDURE [codeModuleName: String, numberOfOutputFiles: INT ← 1] =
BEGIN
OutputFileCount ← numberOfOutputFiles;
IF OutputFileCount = 1 THEN {
codeStream[1] ← FS.StreamOpen[
fileName: ModuleDotMesa[codeModuleName],
accessOptions: $create ];
codeStreamIndex[1] ← 0;
}
ELSE {
nameBeforeImpl: String = codeModuleName.Substr[0, codeModuleName.Length[]-4];
FOR no: INT IN [1..OutputFileCount] DO
name: String = nameBeforeImpl.Cat[Convert.RopeFromInt[from: no, showRadix: FALSE] , "Impl.mesa"];
codeStream[no] ← FS.StreamOpen[
fileName: name,
accessOptions: $create ];
codeStreamIndex[no] ← 0;
ENDLOOP;
};
InitializeCodeLinePut;
CurrentOutputFile ← 0 ;
END;
OutputToAllFiles: PUBLIC PROCEDURE = {
CurrentOutputFile ← 0 ;
} ;
NextOutputFile: PUBLIC PROCEDURE = {
CurrentOutputFile ← CurrentOutputFile + 1;
IF CurrentOutputFile > OutputFileCount THEN CurrentOutputFile ← 1;
};
CodeStreamPut: PROCEDURE [char: CHARACTER] =
INLINE BEGIN
IF CurrentOutputFile = 0 THEN {
FOR no: INT IN [1..OutputFileCount] DO
codeStream[no].PutChar[char];
codeStreamIndex[no] ← codeStreamIndex[no] + 1;
ENDLOOP;
}
ELSE {
codeStream[CurrentOutputFile].PutChar[char];
codeStreamIndex[CurrentOutputFile] ← codeStreamIndex[CurrentOutputFile] + 1;
};
END;
CodeStreamPutRope: PROCEDURE [rope: String] =
INLINE BEGIN
IF CurrentOutputFile = 0 THEN {
FOR no: INT IN [1..OutputFileCount] DO
codeStream[no].PutRope[rope];
codeStreamIndex[no] ← codeStreamIndex[no] + rope.Length[];
ENDLOOP;
}
ELSE {
codeStream[CurrentOutputFile].PutRope[rope];
codeStreamIndex[CurrentOutputFile] ← codeStreamIndex[CurrentOutputFile] + rope.Length[];
};
END;
GetCodeFileName: PROC RETURNS [String] = INLINE {
RETURN[IF IsNull[codeFilename] THEN StringNIL ELSE codeFilename ]};
GetCodeStreamIndex: PROC RETURNS [LONG INTEGER] =
INLINE {IF CurrentOutputFile = 0 THEN RETURN [codeStreamIndex[1]] ELSE RETURN[codeStreamIndex[CurrentOutputFile]]};
StampCodeFile: PUBLIC PROCEDURE [codeModuleName:String] =
BEGIN
moduleName, fileName: String;
now: ST.GMT = BasicTime.Now[];
year: INT = BasicTime.Unpack[now].year;
lupineTime: ST.GMT ← Loader.BCDBuildTime[];
moduleVersion: ST.VersionStamp;
moduleTime, sourceTime: ST.GMT;
[moduleName: moduleName, fileName: fileName, moduleVersion: moduleVersion,
moduleCreateTime: moduleTime, sourceCreateTime: sourceTime]
ST.GetInterfaceInfo[];
CodeStreamPutRope[IO.PutFR["-- Copyright (C) %g by Xerox Corporation. All rights reserved.\n", [integer[year]]]];
CodeStreamPutRope[IO.PutFR[
"-- Stub file %g was translated on %g by Lupine of %g",
[rope[codeFilename]],
[time[now]],
[time[lupineTime]] ]];
CodeStreamPutRope[IO.PutFR["\n-- Source interface %g came from file %g, which was created on %g with version stamp %g from source of %g.\n",
[rope[moduleName]],
[rope[fileName]],
[time[moduleTime]],
[rope[ST.VersionStampString[stamp: moduleVersion]]],
[time[sourceTime]] ]];
END;
StampTranslationOptions: PUBLIC PROCEDURE [options: Options] =
BEGIN
LanguageString: ARRAY Language OF String = [Cedar: "Cedar", Mesa: "Mesa"];
ParamPassingString: ARRAY ParamPassingMethod OF String = [
Var: "VAR", Value: "VALUE", Result: "RESULT",
Handle: "HANDLE", InterMds: "InterMds" ];
BoolString: ARRAY BOOLEAN OF String = [TRUE: "TRUE", FALSE: "FALSE"];
oldest, newest: LONG INTEGER;
[oldestSupported: oldest, newestSupported: newest] ← Marshal.ParameterProtocolVersions[];
CodeStreamPutRope[IO.PutFR["
-- The parameters for this translation are:
-- Target language = %g
-- Default parameter passing = %g
-- Deallocate server heap arguments = %g
-- Inline RpcServerImpl dispatcher stubs = %g
-- Declare signals = %g",
[rope[LanguageString[options.targetLanguage]]],
[rope[ParamPassingString[options.defaultParamPassing]]],
[rope[BoolString[options.freeServerArguments]]],
[rope[BoolString[options.inlineServerDispatcherStubs]]],
[rope[BoolString[options.declareSignals]]]
]];
CodeStreamPutRope[IO.PutFR["
-- Warn about short POINTER (\"MDS\") allocations = %g
-- Maximum number of dynamic heap NEWs = %g, MDS NEWs = %g
-- Acceptable parameter protocols = VersionRange[%g..%g].\n",
[rope[BoolString[options.warnMDSAllocs]]],
[integer[options.maxHeapAllocations]],
[integer[options.maxMdsAllocations]],
[integer[oldest]],
[integer[newest]]
]];
END; -- StampTranslationOptions.
CloseCodeFile: PUBLIC PROCEDURE =
It is VERY important to call CloseCodeFile on any relevant UNWINDs.
BEGIN
FinalizeCodeLinePut[];
FOR no: INT IN [1..OutputFileCount] DO
file: FS.OpenFile;
bytes: INT;
created: BasicTime.GMT ← BasicTime.earliestGMT;
codeStream[no].Flush[];
Insure that the generated mesa files do not have the same date. This is to help the version map work better.
file ← FileStream.OpenFileFromStream[codeStream[no]];
[bytes: bytes, created: created] ← file.GetInfo[];
IF BasicTime.Period[from: LastCodeFileGMT, to: created] <= 0 THEN {
created ← BasicTime.Update[base: LastCodeFileGMT, period: 1];
file.SetByteCountAndCreatedTime[bytes: bytes, created: created];
};
LastCodeFileGMT ← created;
codeStream[no].Close[];
codeStreamIndex[no] ← 0;
ENDLOOP;
codeFilename ← NIL;
END;
DeleteExistingCodeFiles: PROCEDURE =
BEGIN
DeleteOne: PROC [module: String] =
BEGIN
FS.Delete [
name: ModuleDotMesa[module]
! FS.Error => CONTINUE ];
END;
DeleteOne[ModuleName[control]];
DeleteOne[ModuleName[client]];
DeleteOne[ModuleName[server]];
END;
ModuleDotMesa: PROCEDURE [moduleName: String]
RETURNS [String] =
INLINE BEGIN
RETURN[moduleName.Cat[".mesa"]]
END;
Simple formatting routines for indenting and wrapping code lines.
SpacesPerNest: INTEGER = 2;
SpacesPerTab: INTEGER = 4;
ExtraSpacesOnWrap: INTEGER = 2*SpacesPerNest;
ApproxMaxIdentifierLength: INTEGER = 12;
stillLeadingSpaces, doingComment: BOOLEAN;
lineLength, spaces, lastHyphen, lineWrapThreshhold: INTEGER;
formattedStream: PUBLIC IO.STREAMIO.CreateStream[
streamProcs: IO.CreateStreamProcs[variety: output, class: $Lupine, putChar: CodeLinePut],
streamData: NIL];
SetCodeLineLength: PROCEDURE [desiredLineLength: LONG INTEGER] =
INLINE BEGIN
lineWrapThreshhold ← MAX[
28,
SHORT[desiredLineLength]-ApproxMaxIdentifierLength-ExtraSpacesOnWrap ];
END;
InitializeCodeLinePut, StartNewCodeLine: PROCEDURE =
INLINE BEGIN
stillLeadingSpaces ← TRUE;
lineLength ← spaces ← 0;
lastHyphen ← -1;
doingComment ← FALSE;
END;
FinalizeCodeLinePut: PROCEDURE = INLINE {};
CodeLinePut: SAFE PROCEDURE[self: IO.STREAM, char: CHAR] = TRUSTED
This routine WILL wrap comments and string constants. Lupine
generates no troublesome strings (i.e., strings that are too long
or contain one or more hyphens). Comments should be wrapped OK.
The BS character is specially interpreted to mean start a new line
and indent for a continuation (as in a multiline record declaration).
BEGIN
WrapLine: PROC =
If the current line is indented all the way to the RIGHT margin
(lineWrapThreshhold), any further attempts to indent are ignored.
BEGIN
IF stillLeadingSpaces
THEN lineLength ← spaces ← lineWrapThreshhold
ELSE BEGIN
lineLength ← spaces;
IF spaces >= lineWrapThreshhold THEN stillLeadingSpaces ← TRUE;
CodeStreamPut[Ascii.CR];
CodeStreamPutRope[IndentInline[(spaces+ExtraSpacesOnWrap)/SpacesPerNest]];
IF doingComment THEN {
CodeStreamPutRope["-- "];
lineLength ← lineLength + 3; lastHyphen ← -1 };
END;
END; -- WrapLine.
ExtendToNextTabStop: PROC [length: INTEGER]
RETURNS [--lengthAtStop:-- INTEGER] =
INLINE {RETURN[SpacesPerTab*(length/SpacesPerTab+1)]};
SELECT char FROM
Ascii.SP =>
IF lineLength < lineWrapThreshhold
THEN {
lineLength ← lineLength + 1;
IF stillLeadingSpaces THEN spaces ← spaces + 1;
CodeStreamPut[Ascii.SP] }
ELSE WrapLine;
Ascii.TAB =>
IF lineLength < lineWrapThreshhold
THEN {
lineLength ← ExtendToNextTabStop[lineLength];
IF stillLeadingSpaces THEN spaces ← ExtendToNextTabStop[spaces];
CodeStreamPut[Ascii.TAB] }
ELSE WrapLine;
Ascii.CR, Ascii.FF => {
StartNewCodeLine[];
CodeStreamPut[char] };
Ascii.BS => WrapLine; -- Special; BS not printed.
ENDCASE => {
stillLeadingSpaces ← FALSE;
lineLength ← lineLength + 1;
IF char = '- THEN {
IF lastHyphen = lineLength-1
THEN doingComment ← ~doingComment ELSE lastHyphen ← lineLength };
CodeStreamPut[char] };
END; -- CodeLinePut.
WF1: PUBLIC PROC[format: Rope.ROPE, v1: IO.Value] =
BEGIN
formattedStream.PutF[format, v1];
END;
WFS, WriteFormattedStrings: PUBLIC PROCEDURE [
s01, s02, s03: String←StringNIL ] =
BEGIN
IF s01 # StringNIL THEN formattedStream.PutRope[s01];
IF s02 # StringNIL THEN formattedStream.PutRope[s02];
IF s03 # StringNIL THEN formattedStream.PutRope[s03];
END;
WFSL, WriteFormattedStringsLong: PUBLIC PROCEDURE [
s01, s02, s03, s04, s05, s06, s07, s08, s09, s10,
s11, s12, s13, s14, s15, s16, s17, s18: String←StringNIL] =
BEGIN
IF s01 # StringNIL THEN formattedStream.PutRope[s01];
IF s02 # StringNIL THEN formattedStream.PutRope[s02];
IF s03 # StringNIL THEN formattedStream.PutRope[s03];
IF s04 # StringNIL THEN formattedStream.PutRope[s04];
IF s05 # StringNIL THEN formattedStream.PutRope[s05];
IF s06 # StringNIL THEN formattedStream.PutRope[s06];
IF s07 # StringNIL THEN formattedStream.PutRope[s07];
IF s08 # StringNIL THEN formattedStream.PutRope[s08];
IF s09 # StringNIL THEN formattedStream.PutRope[s09];
IF s10 # StringNIL THEN formattedStream.PutRope[s10];
IF s11 # StringNIL THEN formattedStream.PutRope[s11];
IF s12 # StringNIL THEN formattedStream.PutRope[s12];
IF s13 # StringNIL THEN formattedStream.PutRope[s13];
IF s14 # StringNIL THEN formattedStream.PutRope[s14];
IF s15 # StringNIL THEN formattedStream.PutRope[s15];
IF s16 # StringNIL THEN formattedStream.PutRope[s16];
IF s17 # StringNIL THEN formattedStream.PutRope[s17];
IF s18 # StringNIL THEN formattedStream.PutRope[s18];
END;
WFL, WriteFormattedLine: PUBLIC PROCEDURE [
nest: Nest, s01, s02, s03: String←StringNIL ] =
BEGIN
formattedStream.PutRope[IndentInline[nest]];
WFS[s01, s02, s03];
formattedStream.PutChar[Ascii.CR];
END;
WFL1, WriteFormattedLine1: PUBLIC PROCEDURE [nest: Nest, s01: String] =
BEGIN
formattedStream.PutRope[IndentInline[nest]];
formattedStream.PutRope[s01];
formattedStream.PutChar[Ascii.CR];
END;
WFLL, WriteFormattedLineLong: PUBLIC PROCEDURE [
nest: Nest, s01, s02, s03, s04, s05, s06: String←StringNIL ] =
BEGIN
formattedStream.PutRope[IndentInline[nest]];
WFSL[s01, s02, s03, s04, s05, s06];
formattedStream.PutChar[Ascii.CR];
END;
Indent: PUBLIC PROCEDURE [nest: Nest] RETURNS [--tabsAndSpaces:-- String] =
BEGIN
RETURN[ IndentInline[nest] ];
END;
IndentInline: PROCEDURE [nest: Nest] RETURNS [--tabsAndSpaces:-- String] =
INLINE BEGIN
RETURN[ NestTable[MIN[nest,LAST[NestTableIndex]]] ];
END;
NestTableIndex: TYPE = Nest[0..30];
NestTable: PACKED ARRAY NestTableIndex OF String = [
These constants must have SpacesPerNest spaces per nest.
--00-- "",
--01-- " ",
--02-- " ",
--03-- " ",
--04-- " ",
--05-- " ",
--06-- " ",
--07-- " ",
--08-- " ",
--09-- " ",
--10-- " ",
--11-- " ",
--12-- " ",
--13-- " ",
--14-- " ",
--15-- " ",
--16-- " ",
--17-- " ",
--18-- " ",
--19-- " ",
--20-- " ",
--21-- " ",
--22-- " ",
--23-- " ",
--24-- " ",
--25-- " ",
--26-- " ",
--27-- " ",
--28-- " ",
--29-- " ",
--30-- " " ];
No module initialization.
END. -- LupineManagerImpl.