-- KipperCodeGenImpl.mesa: December 1, 1985 5:04:27 pm PST -- Sturgis, January 3, 1986 2:25:14 pm PST DIRECTORY Convert USING[RopeFromCard], FS USING[StreamOpen], IO USING[Close, PutF, rope, RopeFromROS, ROS, STREAM], KipperCodeGen USING[], KipperMain1Def USING[NameNode, TypeContextNode, TypeNode], Process USING[Pause, SecondsToTicks], Rope USING[Cat, ROPE], TypeGraphDef USING[BuildRopeName, BuiltInTypeCase, CreateHashTable, EnumerateHashTable, EqualNames, FindEntry, GenAllNamesForType, GenDefFileNames, GenEnumeratedElements, GenImplFileNames, GenNamedTypes, GenNamingNodeLeftNames, GenNamingNodeRightNames, GenRecordFields, GenRootTypeNames, GetBuiltInTypeCase, GetDefFileOfNamedType, GetListTypeValue, GetNameNodeInfo, GetNamedType, GetNamingNodeNamedNode, GetNamingNodePrimaryName, GetRecordCase, GetRefTypeTarget, GetSeqTypeCountName, GetSeqTypeCountType, GetSeqFieldType, GetSeqTypeFieldType, GetTypeNodeCase, GetTypeNodeTypeDefFile, GetTypeNodeFcnDefFile, GetTypeNodeFcnImplFile, GetTypeNodeName, HashTable, MakeEntry, TypeNodeCase]; KipperCodeGenImpl: CEDAR PROGRAM IMPORTS Convert, FS, IO, Process, Rope, TypeGraphDef EXPORTS KipperCodeGen = BEGIN OPEN KipperMain1Def, TypeGraphDef; PrintTypeNodeContext: PUBLIC PROC[context: TypeContextNode, on: IO.STREAM] = BEGIN SeeOneName: PROC[name: NameNode] = BEGIN nameText: Rope.ROPE _ GetNameNodeInfo[name].text; type: TypeNode _ GetNamedType[context, name]; from: NameNode _ GetDefFileOfNamedType[context, name]; fromText: Rope.ROPE _ GetNameNodeInfo[from].text; case: TypeNodeCase _ GetTypeNodeCase[type]; IO.PutF[on, "%g: Type%g = ", IO.rope[nameText], IO.rope[IF fromText = NIL THEN "" ELSE Rope.Cat[" From ", fromText]]]; PrintTypeNodeData[type, 3, on]; END; GenRootTypeNames[context, SeeOneName]; END; PrintTypeNodeData: PROC[type: TypeNode, indent: CARDINAL, on: IO.STREAM] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM naming => IO.PutF[on, "naming \"%g\"\N", IO.rope[FormNameRope[type]]]; enumerated => IO.PutF[on, "enumerated = {%g}\N", IO.rope[FormElementNames[type]]]; record => {IO.PutF[on, "record\N"]; PrintRecordFields[type, indent+3, on]}; ref => {IO.PutF[on, "ref "]; PrintTypeNodeData[GetRefTypeTarget[type], indent+3, on]}; any => IO.PutF[on, "any\N"]; list => {IO.PutF[on, "list of "]; PrintTypeNodeData[GetListTypeValue[type], indent+3, on]}; builtIn => IO.PutF[on, "builtIn\N"]; ENDCASE => ERROR; END; PrintRecordFields: PROC[type: TypeNode, indent: CARDINAL, on: IO.STREAM] = BEGIN PrintOneField: PROC[name: NameNode, type: TypeNode, last: BOOLEAN] = BEGIN PrintIndent[indent, on]; IO.PutF[on, "%g: ", IO.rope[GetNameNodeInfo[name].text]]; PrintTypeNodeData[type, indent+3, on]; END; GenRecordFields[type, PrintOneField]; END; FormNameRope: PROC[type: TypeNode] RETURNS[Rope.ROPE] = BEGIN rope: Rope.ROPE _ GetNameNodeInfo[GetNamingNodePrimaryName[type]].text; SeeRightName: PROC[name: NameNode] = {rope _ Rope.Cat[rope, ".", GetNameNodeInfo[name].text]}; SeeLeftName: PROC[name: NameNode] = {rope _ Rope.Cat[GetNameNodeInfo[name].text, " ", rope]}; GenNamingNodeRightNames[type, SeeRightName]; GenNamingNodeLeftNames[type, SeeLeftName]; RETURN[rope]; END; FormElementNames: PROC[type: TypeNode] RETURNS[Rope.ROPE] = BEGIN rope: Rope.ROPE _ NIL; SeeOneName: PROC[name: NameNode] = BEGIN IF rope # NIL THEN rope _ Rope.Cat[rope, ", "]; rope _ Rope.Cat[rope, GetNameNodeInfo[name].text]; END; GenEnumeratedElements[type, SeeOneName]; RETURN[rope]; END; PrintIndent: PROC[indent: CARDINAL, on: IO.STREAM] = {FOR I: CARDINAL IN [0..indent) DO IO.PutF[on, " "] ENDLOOP}; GeneratorContext: TYPE = REF GeneratorContextBody; GeneratorContextBody: TYPE = RECORD[ defFiles: HashTable, implFiles: HashTable, kipperedTypes: HashTable, functions: HashTable, workList: WorkCell, refAnyWorkCell: WorkCell]; KipperedTypeEntry: TYPE = REF KipperedTypeEntryBody; KipperedTypeEntryBody: TYPE = RECORD[ type: TypeNode, fnNames: ARRAY KipperContext OF RECORD[ kipperFunctionName: NameNode, unKipperFunctionName: NameNode]]; FunctionEntry: TYPE = REF FunctionEntryBody; FunctionEntryBody: TYPE = RECORD[ defFile: NameNode, implFile: NameNode]; WorkCell: TYPE = REF WorkCellBody; WorkCellBody: TYPE = RECORD[ type: TypeNode, mcontext: KipperContext, next: WorkCell]; KipperContext: TYPE = {self, refTarget, listTarget}; KipperUnKipper: TYPE = {kipper, unKipper}; FieldExpCase: TYPE = {localVal, ref, list, shortField, longField}; FormValExp: PROC[exp: Rope.ROPE, case: FieldExpCase] RETURNS[Rope.ROPE] = BEGIN RETURN[SELECT case FROM ref => Rope.Cat["(", exp, "^)"], list => ERROR, localVal, shortField, longField => exp, ENDCASE => ERROR]; END; FormSubFieldCase: PROC[case: FieldExpCase] RETURNS[FieldExpCase] = BEGIN RETURN[SELECT case FROM ref, list, longField => longField, shortField => shortField, localVal => ERROR, ENDCASE => ERROR]; END; BuildFilesFromContext: PUBLIC PROC[context: TypeContextNode, ReportStream: IO.STREAM] = BEGIN gContext: GeneratorContext _ NEW[GeneratorContextBody]; PrepareOneImplFile: PROC[fileName: NameNode] = BEGIN implFileCode: Code _ BuildEmptyCode[context, fileName, impl]; RecordCedarTypeUse[implFileCode, "RawBytes", "Basics"]; RecordCedarTypeUse[implFileCode, "STREAM", "IO"]; RecordCedarProcUse[implFileCode, "UnsafePutBlock", "IO"]; RecordCedarProcUse[implFileCode, "UnsafeGetBlock", "IO"]; RecordCedarTypeUse[implFileCode, "Kipperer", "KipperSupport"]; RecordCedarTypeUse[implFileCode, "UnKipperer", "KipperSupport"]; MakeEntry[gContext.implFiles, fileName, implFileCode]; END; PrepareOneDefFile: PROC[fileName: NameNode] = BEGIN defFileCode: Code _ BuildEmptyCode[context, fileName, def]; DeclareAType: PROC[type: TypeNode] = BEGIN IF EqualNames[GetTypeNodeTypeDefFile[type], fileName] THEN BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM ref, list, enumerated, record, naming => InstallNamedTypeDecl[defFileCode, type]; builtIn, any => NULL; ENDCASE => ERROR; END; END; RecordCedarTypeUse[defFileCode, "Kipperer", "KipperSupport"]; RecordCedarTypeUse[defFileCode, "UnKipperer", "KipperSupport"]; GenNamedTypes[context, DeclareAType]; MakeEntry[gContext.defFiles, fileName, defFileCode]; END; PutNamedRefOrListTypeOnWorkList: PROC[type: TypeNode] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM list, ref => [] _ AddTypeToWorkList[gContext, type, self, kipper]; naming, enumerated, builtIn, record => NULL; ENDCASE => ERROR; END; ConditionalPutRecordTypeOnOnWorkList: PROC[type: TypeNode] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM record => BEGIN name: NameNode _ GetTypeNodeName[type]; -- perhaps already covered by a named ref IF FindEntry[gContext.kipperedTypes, name] = NIL THEN BEGIN -- no, not already covered [] _ AddTypeToWorkList[gContext, type, refTarget, kipper]; END; END; naming, enumerated, builtIn, list, ref => NULL; ENDCASE => ERROR; END; -- initialize the generator context gContext.defFiles _ CreateHashTable[5]; gContext.implFiles _ CreateHashTable[5]; gContext.kipperedTypes _ CreateHashTable[5]; gContext.functions _ CreateHashTable[5]; gContext.workList _ NIL; -- now initialize code for the output files GenImplFileNames[context, PrepareOneImplFile]; GenDefFileNames[context, PrepareOneDefFile]; -- put named ref and list types on the work list GenNamedTypes[context, PutNamedRefOrListTypeOnWorkList]; -- now add named record types which are not the target of named ref types GenNamedTypes[context, ConditionalPutRecordTypeOnOnWorkList]; -- now do the work WHILE gContext.workList # NIL DO cell: WorkCell _ gContext.workList; gContext.workList _ cell.next; -- watch out for ref any, and do it last -- so that all ref types will be known IF GetTypeNodeCase[cell.type] = builtIn AND GetBuiltInTypeCase[cell.type] = Any THEN BEGIN IF gContext.refAnyWorkCell # NIL THEN ERROR; gContext.refAnyWorkCell _ cell; cell.next _ NIL; END ELSE GenCodeForOneProcPair[gContext, cell.type, cell.mcontext] ENDLOOP; IF gContext.refAnyWorkCell # NIL THEN GenCodeForOneProcPair[gContext, gContext.refAnyWorkCell.type, gContext.refAnyWorkCell.mcontext]; -- finally, its time to put out the files BEGIN PutOutOneDefFile: PROC[codeRef: REF ANY, name: NameNode] = BEGIN code: Code _ NARROW[codeRef]; fullFileNameText: Rope.ROPE _ Rope.Cat[GetNameNodeInfo[name].text, ".mesa"]; fileStream: IO.STREAM _ FS.StreamOpen[fullFileNameText, create]; ShowCode[code, fileStream]; IO.Close[fileStream]; IO.PutF[ReportStream, "\Ndefinitions written on %g\N", IO.rope[fullFileNameText]]; Process.Pause[Process.SecondsToTicks[2]]; -- make sure next not same create date END; PutOutOneImplFile: PROC[codeRef: REF ANY, name: NameNode] = BEGIN code: Code _ NARROW[codeRef]; fullFileNameText: Rope.ROPE _ Rope.Cat[GetNameNodeInfo[name].text, ".mesa"]; fileStream: IO.STREAM _ FS.StreamOpen[fullFileNameText, create]; ShowCode[code, fileStream]; IO.Close[fileStream]; IO.PutF[ReportStream, "\Nimplementation written on %g\N", IO.rope[fullFileNameText]]; Process.Pause[Process.SecondsToTicks[2]]; -- make sure next not same create date END; EnumerateHashTable[gContext.defFiles, PutOutOneDefFile]; EnumerateHashTable[gContext.implFiles, PutOutOneImplFile]; END; END; AddTypeToWorkList: PROC[gContext: GeneratorContext, type: TypeNode, mContext: KipperContext, mm: KipperUnKipper] RETURNS[NameNode] = BEGIN typeName: NameNode _ GetTypeNodeName[type]; kipperEntry: KipperedTypeEntry _ NARROW[FindEntry[gContext.kipperedTypes, typeName]]; IF kipperEntry = NIL THEN BEGIN kipperEntry _ NEW[KipperedTypeEntryBody]; kipperEntry.type _ type; MakeEntry[gContext.kipperedTypes, typeName, kipperEntry]; END; IF kipperEntry.fnNames[mContext].kipperFunctionName = NIL THEN BEGIN -- not currently on the work list final: Rope.ROPE _ SELECT mContext FROM self => "", refTarget => "Ref", listTarget => "List", ENDCASE => ERROR; cell: WorkCell _ NEW[WorkCellBody_[type, mContext, gContext.workList]]; kipperFnName: NameNode _ BuildRopeName[Rope.Cat["Kipper", GetNameNodeInfo[typeName].text, final]]; unKipperFnName: NameNode _ BuildRopeName[Rope.Cat["UnKipper", GetNameNodeInfo[typeName].text, final]]; defFileName: NameNode _ GetTypeNodeFcnDefFile[type]; implFileName: NameNode _ GetTypeNodeFcnImplFile[type]; kipperEntry.fnNames[mContext].kipperFunctionName _ kipperFnName; kipperEntry.fnNames[mContext].unKipperFunctionName _ unKipperFnName; gContext.workList _ cell; AddFunctionInfo[gContext, kipperFnName, defFileName, implFileName]; AddFunctionInfo[gContext, unKipperFnName, defFileName, implFileName]; END; RETURN[IF mm = kipper THEN kipperEntry.fnNames[mContext].kipperFunctionName ELSE kipperEntry.fnNames[mContext].unKipperFunctionName]; END; GenKipperedTypes: PROC[gContext: GeneratorContext, marshUnMarsh: KipperUnKipper, for: PROC[TypeNode, KipperContext, NameNode]] = BEGIN SeeOneKipperedType: PROC[ref: REF ANY, name: NameNode] = BEGIN entry: KipperedTypeEntry _ NARROW[ref]; FOR mc: KipperContext IN KipperContext DO IF entry.fnNames[mc].kipperFunctionName # NIL THEN SELECT marshUnMarsh FROM kipper => for[entry.type, mc, entry.fnNames[mc].kipperFunctionName]; unKipper => for[entry.type, mc, entry.fnNames[mc].unKipperFunctionName]; ENDCASE => ERROR; ENDLOOP; END; EnumerateHashTable[gContext.kipperedTypes, SeeOneKipperedType]; END; AddFunctionInfo: PROC[gContext: GeneratorContext, fnName: NameNode, defFile: NameNode, implFile: NameNode] = BEGIN entry: FunctionEntry _ NEW[FunctionEntryBody_[defFile, implFile]]; MakeEntry[gContext.functions, fnName, entry]; END; GetFunctionInfo: PROC[gContext: GeneratorContext, fnName: NameNode] RETURNS[defFile: NameNode, implFile: NameNode] = BEGIN entry: FunctionEntry _ NARROW[FindEntry[gContext.functions, fnName]]; RETURN[entry.defFile, entry.implFile]; END; GenCodeForOneProcPair: PROC[gContext: GeneratorContext, type: TypeNode, mContext: KipperContext] = BEGIN typeNameText: Rope.ROPE _ GetNameNodeInfo[GetTypeNodeName[type]].text; varName: Rope.ROPE _ SELECT mContext FROM self => Rope.Cat[typeNameText, "Val"], refTarget => Rope.Cat[typeNameText, "Ref"], listTarget => Rope.Cat[typeNameText, "List"], ENDCASE => ERROR; varType: Rope.ROPE _ SELECT mContext FROM self => typeNameText, refTarget => Rope.Cat["REF ", typeNameText], listTarget => Rope.Cat["LIST OF ", typeNameText] ENDCASE => ERROR; kipperEntry: KipperedTypeEntry _ NARROW[FindEntry[gContext.kipperedTypes, GetTypeNodeName[type]]]; kipperFnName: NameNode _ kipperEntry.fnNames[mContext].kipperFunctionName; unKipperFnName: NameNode _ kipperEntry.fnNames[mContext].unKipperFunctionName; defFileName: NameNode; implFileName: NameNode; defFileCode: Code; implFileCode: Code; [defFileName, implFileName] _ GetFunctionInfo[gContext, kipperFnName]; defFileCode _ NARROW[FindEntry[gContext.defFiles, defFileName]]; implFileCode _ NARROW[FindEntry[gContext.implFiles, implFileName]]; -- add the proc declarations to the def file AppendRopeCodeStatement[defFileCode, Rope.Cat[ Rope.Cat[ GetNameNodeInfo[kipperFnName].text, ": PROC[kipperer: Kipperer, ", varName], Rope.Cat[ ": ", varType, "];"]]]; AppendRopeCodeStatement[defFileCode, Rope.Cat[ Rope.Cat[ GetNameNodeInfo[unKipperFnName].text, ": PROC[unKipperer: UnKipperer] RETURNS[", varName], Rope.Cat[ ": ", varType, "];"]]]; -- now add the implementations to the impl file AppendOpenRopeCodeStatement[implFileCode, Rope.Cat[ Rope.Cat[ "\N\N", GetNameNodeInfo[kipperFnName].text, ": PUBLIC PROC[kipperer: Kipperer, ", varName], Rope.Cat[ ": ", varType, "] = TRUSTED"]]]; AppendRopeCodeStatement[implFileCode, "BEGIN"]; AppendNeededProcVars[implFileCode]; -- now generate the code body, except that the body of KipperANYRef must be treated special IF GetTypeNodeCase[type] = builtIn AND GetBuiltInTypeCase[type] = Any THEN BEGIN -- this is the KipperANYRef case IF mContext # refTarget THEN ERROR; KipperABuiltIn[gContext, implFileCode, type, varName, ref]; END ELSE BEGIN SELECT mContext FROM self => GenKipperCodeForAType[gContext, implFileCode, type, varName, localVal]; refTarget => KipperARef[gContext, implFileCode, type, varName, localVal]; listTarget => KipperAList[gContext, implFileCode, type, varName, localVal]; ENDCASE => ERROR; END; AppendClosingRopeCodeStatement[implFileCode, "END;"]; AppendOpenRopeCodeStatement[implFileCode, Rope.Cat[ Rope.Cat[ "\N\N", GetNameNodeInfo[unKipperFnName].text, ": PUBLIC PROC[unKipperer: UnKipperer] RETURNS[", varName], Rope.Cat[ ": ", varType, "] = TRUSTED"]]]; AppendRopeCodeStatement[implFileCode, "BEGIN"]; AppendNeededProcVars[implFileCode]; -- now generate the code body, except that the body of UnKipperANYRef must be treated special IF GetTypeNodeCase[type] = builtIn AND GetBuiltInTypeCase[type] = Any THEN BEGIN IF mContext # refTarget THEN ERROR; UnKipperABuiltIn[gContext, implFileCode, type, varName, ref]; END ELSE BEGIN SELECT mContext FROM self => GenUnKipperCodeForAType[gContext, implFileCode, type, varName, localVal]; refTarget => UnKipperARef[gContext, implFileCode, type, varName, localVal]; listTarget => UnKipperAList[gContext, implFileCode, type, varName, localVal]; ENDCASE => ERROR; END; AppendClosingRopeCodeStatement[implFileCode, "END;"]; -- finally, record info needed to construct file headers RecordNamedTypeUse[defFileCode, type]; RecordNamedTypeUse[implFileCode, type]; RecordFunctionExport[implFileCode, gContext, kipperFnName]; RecordFunctionExport[implFileCode, gContext, unKipperFnName]; END; InstallNamedTypeDecl: PROC[code: Code, type: TypeNode] = BEGIN someNamesSeen: BOOLEAN _ FALSE; nameRope: Rope.ROPE _ NIL; SeeOneName: PROC[name: NameNode] = BEGIN IF someNamesSeen THEN nameRope _ Rope.Cat[nameRope, ", "] ELSE someNamesSeen _ TRUE; nameRope _ Rope.Cat[nameRope, GetNameNodeInfo[name].text]; END; GenAllNamesForType[type, SeeOneName]; IF NOT someNamesSeen THEN ERROR; AppendRopeCodeStatement[code, Rope.Cat[nameRope, ": TYPE = "]]; InstallTypeConstDecl[code, type]; ExtendRopeCodeStatement[code, ";"]; END; InstallTypeConstDecl: PROC[code: Code, type: TypeNode] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM naming => -- WARNING has to be fixed up for variant records BEGIN targetType: TypeNode _ GetNamingNodeNamedNode[type]; ExtendRopeCodeStatement[code, GetNameNodeInfo[GetTypeNodeName[targetType]].text]; RecordNamedTypeUse[code, targetType]; -- ExtendRopeCodeStatement[code, Rope.Cat[" ",GetNameNodeInfo[GetNamingNodePrimaryName[type]].text]]; END; enumerated => BEGIN someElementsSeen: BOOLEAN _ FALSE; SeeOneElement: PROC[elementName: NameNode] = BEGIN IF someElementsSeen THEN ExtendRopeCodeStatement[code, ", "] ELSE someElementsSeen _ TRUE; ExtendRopeCodeStatement[code, GetNameNodeInfo[elementName].text]; END; ExtendRopeCodeStatement[code, "{"]; GenEnumeratedElements[type, SeeOneElement]; ExtendRopeCodeStatement[code, "}"]; END; record => BEGIN someFieldsSeen: BOOLEAN _ FALSE; SeeOneField: PROC[fieldName: NameNode, fieldType: TypeNode, last: BOOLEAN] = BEGIN IF someFieldsSeen THEN ExtendRopeCodeStatement[code, ","] ELSE someFieldsSeen _ TRUE; AppendRopeCodeStatement[code, Rope.Cat[GetNameNodeInfo[fieldName].text, ": "]]; InstallTypeConstDecl[code, fieldType]; END; ExtendOpenRopeCodeStatement[code, "RECORD["]; GenRecordFields[type, SeeOneField]; ExtendClosingRopeCodeStatement[code, "]"]; END; ref => BEGIN ExtendRopeCodeStatement[code, "REF "]; InstallTypeConstDecl[code, GetRefTypeTarget[type]]; END; any => ExtendRopeCodeStatement[code, "ANY"]; list => BEGIN ExtendRopeCodeStatement[code, "LIST OF "]; InstallTypeConstDecl[code, GetListTypeValue[type]]; END; seq => -- SEQUENCE field in a record constructor BEGIN ExtendRopeCodeStatement[code, "SEQUENCE "]; ExtendRopeCodeStatement[code, GetNameNodeInfo[GetSeqTypeCountName[type]].text]; ExtendRopeCodeStatement[code, ": "]; InstallTypeConstDecl[code, GetSeqTypeCountType[type]]; ExtendRopeCodeStatement[code, " OF "]; InstallTypeConstDecl[code, GetSeqTypeFieldType[type]]; END; builtIn => ERROR; ENDCASE => ERROR; END; InstallNameOrTypeConstructor: PROC[code: Code, type: TypeNode] = BEGIN name: NameNode _ GetTypeNodeName[type]; IF name # NIL THEN ExtendRopeCodeStatement[code, GetNameNodeInfo[name].text] ELSE InstallTypeConstDecl[code, type]; END; AppendNeededProcVars: PROC[code: Code] = BEGIN AppendRopeCodeStatement[code, ""]; AppendRopeCodeStatement[code, "word: CARDINAL _ 0;"]; AppendRopeCodeStatement[code, "doubleWord: LONG CARDINAL _ 0;"]; AppendRopeCodeStatement[code, "refAny: REF ANY _ NIL;"]; AppendRopeCodeStatement[code, "flag: BOOLEAN _ FALSE;"]; AppendRopeCodeStatement[code, ""]; END; GetUltimateTargetType: PROC[type: TypeNode] RETURNS[TypeNode] = BEGIN-- only valid for REF and LIST types case: TypeNodeCase _ GetTypeNodeCase[type]; targetType: TypeNode _ SELECT case FROM ref => GetRefTypeTarget[type], list => GetListTypeValue[type], ENDCASE => ERROR; WHILE GetTypeNodeCase[targetType] = naming DO targetType _ GetNamingNodeNamedNode[targetType] ENDLOOP; RETURN[targetType]; END; GetFinalNamedType: PROC[type: TypeNode] RETURNS[TypeNode] = BEGIN finalType: TypeNode _ type; WHILE GetTypeNodeCase[finalType] = naming DO finalType _ GetNamingNodeNamedNode[finalType] ENDLOOP; RETURN[finalType]; END; KipperAType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; SELECT case FROM naming => KipperAType[gContext, code, GetNamingNodeNamedNode[type], exp, fieldCase]; enumerated, builtIn, record => GenKipperCodeForAType[gContext, code, type, exp, fieldCase]; ref, list => BEGIN -- we would like to call a procedure for these guys if possible -- it is only possible if they are namable, or the target type is . IF GetTypeNodeName[type] # NIL THEN -- this one is namable BEGIN fnName: NameNode _ AddTypeToWorkList[gContext, type, self, kipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[fnNameText, "[kipperer, ", valExp, "];"]]; RecordFunctionCall[code, gContext, fnName]; END ELSE -- can't be named, but perhaps the target type can be named or is ANY BEGIN targetType: TypeNode _ GetUltimateTargetType[type]; targetName: NameNode _ GetTypeNodeName[targetType]; IF GetTypeNodeCase[targetType] = builtIn AND GetBuiltInTypeCase[targetType] = Any THEN BEGIN -- we are handling REF ANY fnName: NameNode _ AddTypeToWorkList[gContext, targetType, refTarget, kipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[fnNameText, "[kipperer, ", valExp, "];"]]; END ELSE IF targetName # NIL THEN BEGIN -- target can be named mContext: KipperContext _ SELECT case FROM ref => refTarget, list => listTarget, ENDCASE => ERROR; fnName: NameNode _ AddTypeToWorkList[gContext, targetType, mContext, kipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[fnNameText, "[kipperer, ", valExp, "];"]]; RecordFunctionCall[code, gContext, fnName]; END ELSE GenKipperCodeForAType[gContext, code, type, exp, fieldCase]; END; END; any => ERROR; -- can't happen ENDCASE => ERROR; END; UnKipperAType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; SELECT case FROM naming => UnKipperAType[gContext, code, GetNamingNodeNamedNode[type], exp, fieldCase]; enumerated, builtIn, record => GenUnKipperCodeForAType[gContext, code, type, exp, fieldCase]; ref, list => BEGIN -- we would like to call a procedure for these guys if possible -- it is only possible if they are namable, or the target type either namable or ANY. IF GetTypeNodeName[type] # NIL THEN -- this one is namable BEGIN fnName: NameNode _ AddTypeToWorkList[gContext, type, self, unKipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ ", fnNameText, "[unKipperer];"]]; RecordFunctionCall[code, gContext, fnName]; END ELSE -- can't be named, but perhaps the target type can be named BEGIN targetType: TypeNode _ GetUltimateTargetType[type]; targetName: NameNode _ GetTypeNodeName[targetType]; IF GetTypeNodeCase[targetType] = builtIn AND GetBuiltInTypeCase[targetType] = Any THEN BEGIN -- we are handling REF ANY fnName: NameNode _ AddTypeToWorkList[gContext, targetType, refTarget, unKipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ ", fnNameText, "[unKipperer];"]]; END ELSE IF targetName # NIL THEN BEGIN -- target can be named mContext: KipperContext _ SELECT case FROM ref => refTarget, list => listTarget, ENDCASE => ERROR; fnName: NameNode _ AddTypeToWorkList[gContext, targetType, mContext, unKipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ ", fnNameText, "[unKipperer];"]]; RecordFunctionCall[code, gContext, fnName]; END ELSE GenUnKipperCodeForAType[gContext, code, type, exp, fieldCase]; END; END; any => ERROR; -- cant happen ENDCASE; END; GenKipperCodeForAType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM ref => KipperARef[gContext, code, GetRefTypeTarget[type], exp, fieldCase]; list => KipperAList[gContext, code, GetListTypeValue[type], exp, fieldCase]; record => KipperARecord[gContext, code, type, exp, fieldCase]; enumerated => KipperAnEnumType[gContext, code, type, exp, fieldCase]; builtIn => KipperABuiltIn[gContext, code, type, exp, fieldCase]; naming => ERROR; -- can't happen ENDCASE => ERROR; END; GenUnKipperCodeForAType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: TypeNodeCase _ GetTypeNodeCase[type]; SELECT case FROM ref => UnKipperARef[gContext, code, GetRefTypeTarget[type], exp, fieldCase]; list => UnKipperAList[gContext, code, GetListTypeValue[type], exp, fieldCase]; record => UnKipperARecord[gContext, code, type, exp, fieldCase]; enumerated => UnKipperAnEnumType[gContext, code, type, exp, fieldCase]; builtIn => UnKipperABuiltIn[gContext, code, type, exp, fieldCase]; naming => ERROR; -- can't happen ENDCASE => ERROR; END; KipperARecord: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN seq: BOOLEAN _ GetRecordCase[type] = seq; subFieldCase: FieldExpCase _ FormSubFieldCase[fieldCase]; OneField: PROC[fieldName: NameNode, fieldType: TypeNode, last: BOOLEAN] = BEGIN fieldTextName: Rope.ROPE _ GetNameNodeInfo[fieldName].text; IF seq AND last THEN KipperASeq[gContext, code, fieldType, exp, fieldCase, fieldTextName] ELSE KipperAType[gContext, code, fieldType, Rope.Cat["(", exp, ".", fieldTextName, ")"], subFieldCase]; END; GenRecordFields[type, OneField]; END; UnKipperARecord: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN seq: BOOLEAN _ GetRecordCase[type] = seq; subFieldCase: FieldExpCase _ FormSubFieldCase[fieldCase]; OneField: PROC[fieldName: NameNode, fieldType: TypeNode, last: BOOLEAN] = BEGIN fieldTextName: Rope.ROPE _ GetNameNodeInfo[fieldName].text; IF seq AND last THEN UnKipperASeq[gContext, code, fieldType, exp, fieldCase, fieldTextName] ELSE UnKipperAType[gContext, code, fieldType, Rope.Cat["(", exp, ".", fieldTextName, ")"], subFieldCase]; END; GenRecordFields[type, OneField]; END; KipperASeq: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase, fieldTextName: Rope.ROPE] = BEGIN -- exp defines the record containing the seq field subFieldCase: FieldExpCase _ FormSubFieldCase[fieldCase]; fieldExp: Rope.ROPE _ Rope.Cat["(", exp, ".", fieldTextName, "[I])"]; countFieldTextName: Rope.ROPE _ GetNameNodeInfo[GetSeqTypeCountName[type]].text; countExp: Rope.ROPE _ Rope.Cat["(", exp, ".", countFieldTextName, ")"]; countFieldType: TypeNode _ GetSeqTypeCountType[type]; fieldType: TypeNode _ GetSeqTypeFieldType[type]; AppendRopeCodeStatement[code, "FOR I: "]; InstallNameOrTypeConstructor[code, countFieldType]; ExtendOpenRopeCodeStatement[code, Rope.Cat[" IN[0..", countExp, ") DO"]]; KipperAType[gContext, code, fieldType, fieldExp, subFieldCase]; AppendClosingRopeCodeStatement[code, "ENDLOOP;"]; END; UnKipperASeq: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase, fieldTextName: Rope.ROPE] = BEGIN subFieldCase: FieldExpCase _ FormSubFieldCase[fieldCase]; fieldExp: Rope.ROPE _ Rope.Cat["(", exp, ".", fieldTextName, "[I])"]; countFieldTextName: Rope.ROPE _ GetNameNodeInfo[GetSeqTypeCountName[type]].text; countExp: Rope.ROPE _ Rope.Cat["(", exp, ".", countFieldTextName, ")"]; countFieldType: TypeNode _ GetSeqTypeCountType[type]; fieldType: TypeNode _ GetSeqTypeFieldType[type]; AppendRopeCodeStatement[code, "FOR I: "]; InstallNameOrTypeConstructor[code, countFieldType]; ExtendOpenRopeCodeStatement[code, Rope.Cat[" IN[0..", countExp, ") DO"]]; UnKipperAType[gContext, code, fieldType, fieldExp, subFieldCase]; AppendClosingRopeCodeStatement[code, "ENDLOOP;"]; END; KipperAnEnumType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat["word _ ORD[", valExp, "];"]]; KipperACardinal[gContext, code, "word", localVal]; END; UnKipperAnEnumType: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; UnKipperACardinal[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ VAL[word];"]]; END; KipperARef: PROC[gContext: GeneratorContext, code: Code, targetType: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; -- REF ANY must be treated different from other refs, since ANY is not a kipperable type. finalTargetType: TypeNode _ GetFinalNamedType[targetType]; IF GetTypeNodeCase[finalTargetType] = builtIn AND GetBuiltInTypeCase[finalTargetType] = Any THEN BEGIN -- we are handling REF ANY -- generate a call on the global ref any kipper procedure fnName: NameNode _ AddTypeToWorkList[gContext, finalTargetType, refTarget, kipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[fnNameText, "[kipperer, ", valExp, "];"]]; END ELSE BEGIN -- normal REF case. seq: BOOLEAN _ GetTypeNodeCase[finalTargetType] = record AND GetRecordCase[finalTargetType] = seq; AppendOpenRopeCodeStatement[code, Rope.Cat["IF KipperRef[kipperer, ", valExp, "] THEN "]]; AppendRopeCodeStatement[code, "BEGIN"]; IF seq THEN -- we must kipper the count BEGIN seqFieldCountType: TypeNode _ IF seq THEN GetSeqTypeCountType[GetSeqFieldType[finalTargetType]] ELSE NIL; seqFieldCountName: NameNode _ IF seq THEN GetSeqTypeCountName[GetSeqFieldType[finalTargetType]] ELSE NIL; SeqFieldCountNameText: Rope.ROPE _ GetNameNodeInfo[seqFieldCountName].text; AppendRopeCodeStatement[code, "count: "]; InstallNameOrTypeConstructor[code, seqFieldCountType]; ExtendRopeCodeStatement[code, Rope.Cat[" _ ", valExp, ".", SeqFieldCountNameText, ";"]]; KipperAType[gContext, code, seqFieldCountType, "count", localVal]; END; KipperAType[gContext, code, finalTargetType, valExp, ref]; AppendClosingRopeCodeStatement[code, "END;"]; RecordCedarProcUse[code, "KipperRef", "KipperSupport"]; END; END; UnKipperARef: PROC[gContext: GeneratorContext, code: Code, targetType: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; -- REF ANY must be treated different from other refs, since ANY is not a kipperable type. finalTargetType: TypeNode _ GetFinalNamedType[targetType]; IF GetTypeNodeCase[finalTargetType] = builtIn AND GetBuiltInTypeCase[finalTargetType] = Any THEN BEGIN -- we are handling REF ANY -- generate a call on the global ref any unkipper procedure fnName: NameNode _ AddTypeToWorkList[gContext, finalTargetType, refTarget, unKipper]; fnNameText: Rope.ROPE _ GetNameNodeInfo[fnName].text; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ ", fnNameText, "[unKipperer];"]]; END ELSE BEGIN -- normal REF case. seq: BOOLEAN _ GetTypeNodeCase[finalTargetType] = record AND GetRecordCase[finalTargetType] = seq; seqFieldCountType: TypeNode _ IF seq THEN GetSeqTypeCountType[GetSeqFieldType[finalTargetType]] ELSE NIL; AppendRopeCodeStatement[code, Rope.Cat["[flag, refAny] _ UnKipperRef[unKipperer];"]]; AppendOpenRopeCodeStatement[code, Rope.Cat["IF flag THEN"]]; AppendRopeCodeStatement[code, "BEGIN"]; IF seq THEN BEGIN -- we must get the count AppendRopeCodeStatement[code, "count: "]; InstallNameOrTypeConstructor[code, seqFieldCountType]; ExtendRopeCodeStatement[code, ";"]; UnKipperAType[gContext, code, seqFieldCountType, "count", localVal]; END; AppendRopeCodeStatement[code, Rope.Cat["refAny _ NEW["]]; InstallNameOrTypeConstructor[code, finalTargetType]; IF seq THEN {ExtendRopeCodeStatement[code, "[count]"]}; ExtendRopeCodeStatement[code, "];"]; AppendRopeCodeStatement[code, Rope.Cat["RecordUnKipperedRef[unKipperer, refAny];"]]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ NARROW[refAny];"]]; UnKipperAType[gContext, code, finalTargetType, exp, ref]; AppendClosingRopeCodeStatement[code, "END"]; AppendOpenRopeCodeStatement[code, " ELSE"]; AppendClosingRopeCodeStatement[code, Rope.Cat[valExp, " _ NARROW[refAny];"]]; RecordCedarProcUse[code, "UnKipperRef", "KipperSupport"]; RecordCedarProcUse[code, "RecordUnKipperedRef", "KipperSupport"]; END; END; KipperAList: PROC[gContext: GeneratorContext, code: Code, valueType: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendOpenRopeCodeStatement[code, "FOR cell: LIST OF "]; InstallNameOrTypeConstructor[code, valueType]; ExtendRopeCodeStatement[code, Rope.Cat[" _ ", valExp, ", cell.rest WHILE cell # NIL DO"]]; AppendRopeCodeStatement[code, "word _ 0;"]; KipperACardinal[gContext, code, "word", localVal]; KipperAType[gContext, code, valueType, Rope.Cat["(cell.first)"], longField]; AppendClosingRopeCodeStatement[code, "ENDLOOP;"]; AppendRopeCodeStatement[code, "word _ 1;"]; KipperACardinal[gContext, code, "word", localVal]; END; UnKipperAList: PROC[gContext: GeneratorContext, code: Code, valueType: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; ExtendOpenRopeCodeStatement[code, ""]; AppendRopeCodeStatement[code, "BEGIN"]; AppendRopeCodeStatement[code, "temp: "]; InstallNameOrTypeConstructor[code, valueType]; ExtendRopeCodeStatement[code, ";"]; AppendRopeCodeStatement[code, "first, last, current: LIST OF "]; InstallNameOrTypeConstructor[code, valueType]; ExtendRopeCodeStatement[code, " _ NIL;"]; UnKipperACardinal[gContext, code, "word", localVal]; AppendOpenRopeCodeStatement[code, "WHILE word = 0 DO"]; UnKipperAType[gContext, code, valueType, "temp", localVal]; AppendRopeCodeStatement[code, "current _ LIST[temp];"]; AppendRopeCodeStatement[code, "IF last = NIL THEN first _ current ELSE last.rest _ current;"]; AppendRopeCodeStatement[code, "last _ current;"]; UnKipperACardinal[gContext, code, "word", localVal]; AppendClosingRopeCodeStatement[code, "ENDLOOP;"]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ first;"]]; AppendClosingRopeCodeStatement[code, "END;"]; END; KipperABuiltIn: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: BuiltInTypeCase _ GetBuiltInTypeCase[type]; SELECT case FROM Cardinal => KipperACardinal[gContext, code, exp, fieldCase]; Int => KipperAnInt[gContext, code, exp, fieldCase]; Boolean => KipperABoolean[gContext, code, exp, fieldCase]; Rope => KipperARope[gContext, code, exp, fieldCase]; Any => KipperAnAny[gContext, code, type, exp, fieldCase]; Bool => KipperABoolean[gContext, code, exp, fieldCase]; Char => KipperAChar[gContext, code, exp, fieldCase]; Character => KipperAChar[gContext, code, exp, fieldCase]; Integer => KipperAnInteger[gContext, code, exp, fieldCase]; Nat => KipperANat[gContext, code, exp, fieldCase]; Real => KipperAReal[gContext, code, exp, fieldCase]; Word => KipperAWord[gContext, code, exp, fieldCase]; Card => KipperACard[gContext, code, exp, fieldCase]; ENDCASE => ERROR; END; UnKipperABuiltIn: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN case: BuiltInTypeCase _ GetBuiltInTypeCase[type]; SELECT case FROM Cardinal => UnKipperACardinal[gContext, code, exp, fieldCase]; Int => UnKipperAnInt[gContext, code, exp, fieldCase]; Boolean => UnKipperABoolean[gContext, code, exp, fieldCase]; Rope => UnKipperARope[gContext, code, exp, fieldCase]; Any => UnKipperAnAny[gContext, code, type, exp, fieldCase]; Bool => UnKipperABoolean[gContext, code, exp, fieldCase]; Char => UnKipperAChar[gContext, code, exp, fieldCase]; Character => UnKipperAChar[gContext, code, exp, fieldCase]; Integer => UnKipperAnInteger[gContext, code, exp, fieldCase]; Nat => UnKipperANat[gContext, code, exp, fieldCase]; Real => UnKipperAReal[gContext, code, exp, fieldCase]; Word => UnKipperAWord[gContext, code, exp, fieldCase]; Card => UnKipperACard[gContext, code, exp, fieldCase]; ENDCASE => ERROR; END; KipperACardinal: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperOneWord[gContext, code, exp, fieldCase]}; UnKipperACardinal: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperOneWord[gContext, code, exp, fieldCase]}; KipperAnInt: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperDoubleWord[gContext, code, exp, fieldCase]}; UnKipperAnInt: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperDoubleWord[gContext, code, exp, fieldCase]}; KipperABoolean: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat["word _ IF", valExp, "THEN 1 ELSE 0;"]]; KipperOneWord[gContext, code, "word", localVal] END; UnKipperABoolean: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; UnKipperOneWord[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ (word=1);"]]; END; KipperARope: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat["KipperRope[kipperer, ", valExp, "];"]]; RecordCedarTypeUse[code, "ROPE", "Rope"]; RecordCedarProcUse[code, "KipperRope", "KipperSupport"]; END; UnKipperARope: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat[valExp, "_ UnKipperRope[unKipperer];"]]; RecordCedarTypeUse[code, "ROPE", "Rope"]; RecordCedarProcUse[code, "UnKipperRope", "KipperSupport"]; END; KipperAnAny: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN IF fieldCase # ref THEN ERROR; AppendOpenRopeCodeStatement[code, Rope.Cat["IF ", exp, " = NIL THEN"]]; AppendRopeCodeStatement[code, "BEGIN"]; AppendRopeCodeStatement[code, "word _ 1;"]; KipperACardinal[gContext, code, "word", localVal]; AppendClosingRopeCodeStatement[code, "END"]; AppendOpenRopeCodeStatement[code, Rope.Cat[" ELSE WITH ", exp, " SELECT FROM"]]; BEGIN index: CARDINAL _ 2; InstallOneEntry: PROC[varName, varTypeText, fnNameText: Rope.ROPE] = BEGIN AppendOpenRopeCodeStatement[code, Rope.Cat[varName, ": ", varTypeText, " => "]]; AppendRopeCodeStatement[code, "BEGIN"]; AppendRopeCodeStatement[code, Rope.Cat["word _ ", Convert.RopeFromCard[index], ";"]]; KipperACardinal[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, Rope.Cat[fnNameText, "[kipperer, ", varName, "];"]]; AppendClosingRopeCodeStatement[code, "END;"]; index _ index + 1; END; SeeOneType: PROC[xType: TypeNode, mContext: KipperContext, fnName: NameNode] = BEGIN -- we must avoid any types that are equivalent to ANY or REF ANY -- ANY itself will always be generated, because its presence on the work list is how we come to be in this procedure in the first place finalXType: TypeNode _ GetFinalNamedType[xType]; IF mContext = self THEN finalXType _ GetUltimateTargetType[finalXType]; IF GetTypeNodeCase[finalXType] # builtIn OR GetBuiltInTypeCase[finalXType] # Any THEN BEGIN varName: Rope.ROPE _ Rope.Cat["x", Convert.RopeFromCard[index]]; typeNameText: Rope.ROPE _ GetNameNodeInfo[GetTypeNodeName[xType]].text; varTypeText: Rope.ROPE _ SELECT mContext FROM self => typeNameText, refTarget => Rope.Cat["REF ", typeNameText], listTarget => Rope.Cat["LIST OF ", typeNameText], ENDCASE => ERROR; InstallOneEntry[varName, varTypeText, GetNameNodeInfo[fnName].text]; END; END; InstallOneEntry["rope", "Rope.ROPE", "KipperRope"]; InstallOneEntry["text", "REF TEXT", "KipperRefText"]; GenKipperedTypes[gContext, kipper, SeeOneType]; END; AppendClosingRopeCodeStatement[code, "ENDCASE => ERROR;"]; RecordCedarProcUse[code, "KipperRope", "KipperSupport"]; RecordCedarProcUse[code, "KipperRefText", "KipperSupport"]; END; UnKipperAnAny: PROC[gContext: GeneratorContext, code: Code, type: TypeNode, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN IF fieldCase # ref THEN ERROR; UnKipperACardinal[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, "IF word = 1 THEN RETURN[NIL]"]; AppendOpenRopeCodeStatement[code, " ELSE SELECT word FROM"]; BEGIN index: CARDINAL _ 2; InstallOneEntry: PROC[fnNameText: Rope.ROPE] = BEGIN AppendRopeCodeStatement[code, Rope.Cat[ Rope.Cat[Convert.RopeFromCard[index], " => "], Rope.Cat[exp, " _ ", fnNameText, "[unKipperer];"]]]; index _ index + 1; END; SeeOneType: PROC[xType: TypeNode, mContext: KipperContext, fnName: NameNode] = BEGIN -- we must avoid any types that are equivalent to ANY or REF ANY -- ANY itself will always be generated, because its presence on the work list is how we come to be in this procedure in the first place finalXType: TypeNode _ GetFinalNamedType[xType]; IF mContext = self THEN finalXType _ GetUltimateTargetType[finalXType]; IF GetTypeNodeCase[finalXType] # builtIn OR GetBuiltInTypeCase[finalXType] # Any THEN InstallOneEntry[GetNameNodeInfo[fnName].text]; END; InstallOneEntry["UnKipperRope"]; InstallOneEntry["UnKipperRefText"]; GenKipperedTypes[gContext, unKipper, SeeOneType]; END; AppendClosingRopeCodeStatement[code, "ENDCASE => ERROR;"]; RecordCedarProcUse[code, "UnKipperRope", "KipperSupport"]; RecordCedarProcUse[code, "UnKipperRefText", "KipperSupport"]; END; KipperAChar: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat["word _ ORD[", valExp, "];"]]; KipperOneWord[gContext, code, "word", localVal] END; UnKipperAChar: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; UnKipperOneWord[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ VAL[word];"]]; END; KipperAnInteger: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperOneWord[gContext, code, exp, fieldCase]}; UnKipperAnInteger: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperOneWord[gContext, code, exp, fieldCase]}; KipperANat: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; AppendRopeCodeStatement[code, Rope.Cat["word _ ", valExp, ";"]]; KipperOneWord[gContext, code, "word", localVal] END; UnKipperANat: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN valExp: Rope.ROPE _ FormValExp[exp, fieldCase]; UnKipperOneWord[gContext, code, "word", localVal]; AppendRopeCodeStatement[code, Rope.Cat[valExp, " _ word;"]]; END; KipperAReal: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperDoubleWord[gContext, code, exp, fieldCase]}; UnKipperAReal: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperDoubleWord[gContext, code, exp, fieldCase]}; KipperAWord: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperOneWord[gContext, code, exp, fieldCase]}; UnKipperAWord: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperOneWord[gContext, code, exp, fieldCase]}; KipperACard: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {KipperDoubleWord[gContext, code, exp, fieldCase]}; UnKipperACard: PROC[gContext: GeneratorContext, code: Code, exp: Rope.ROPE, fieldCase: FieldExpCase] = {UnKipperDoubleWord[gContext, code, exp, fieldCase]}; KipperOneWord: PROC[gContext: GeneratorContext, code: Code, wordExp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN SELECT fieldCase FROM localVal, shortField => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[LONG[@", wordExp, "], LONG POINTER TO RawBytes], 0, 2]];"]]; longField => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[@", wordExp, ", LONG POINTER TO RawBytes], 0, 2]];"]]; ref => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[", wordExp, ", LONG POINTER TO RawBytes], 0, 2]];"]]; ENDCASE => ERROR; END; UnKipperOneWord: PROC[gContext: GeneratorContext, code: Code, wordExp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN SELECT fieldCase FROM localVal, shortField => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[LONG[@", wordExp, "], LONG POINTER TO RawBytes], 0, 2]] # 2 THEN ERROR;"]]; longField => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[@", wordExp, ", LONG POINTER TO RawBytes], 0, 2]] # 2 THEN ERROR;"]]; ref => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[", wordExp, ", LONG POINTER TO RawBytes], 0, 2]] # 2 THEN ERROR;"]]; ENDCASE => ERROR; END; KipperDoubleWord: PROC[gContext: GeneratorContext, code: Code, doubleWordExp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN SELECT fieldCase FROM localVal, shortField => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[LONG[@", doubleWordExp, "], LONG POINTER TO RawBytes], 0, 4]];"]]; longField => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[@", doubleWordExp, ", LONG POINTER TO RawBytes], 0, 4]];"]]; ref => AppendRopeCodeStatement[code, Rope.Cat["UnsafePutBlock[kipperer.stream, [LOOPHOLE[", doubleWordExp, ", LONG POINTER TO RawBytes], 0, 4]];"]]; ENDCASE => ERROR; END; UnKipperDoubleWord: PROC[gContext: GeneratorContext, code: Code, doubleWordExp: Rope.ROPE, fieldCase: FieldExpCase] = BEGIN SELECT fieldCase FROM localVal, shortField => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[LONG[@", doubleWordExp, "], LONG POINTER TO RawBytes], 0, 4]] # 4 THEN ERROR;"]]; longField => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[@", doubleWordExp, ", LONG POINTER TO RawBytes], 0, 4]] # 4 THEN ERROR;"]]; ref => AppendRopeCodeStatement[code, Rope.Cat["IF UnsafeGetBlock[unKipperer.stream, [LOOPHOLE[", doubleWordExp, ", LONG POINTER TO RawBytes], 0, 4]] # 4 THEN ERROR;"]]; ENDCASE => ERROR; END; KipperArrayOfWord: PROC[code: Code, arrayExp, countExp: Rope.ROPE] = BEGIN ERROR END; UnKipperArrayOfWord: PROC[code: Code, arrayExp, countExp: Rope.ROPE] = BEGIN ERROR END; Code: TYPE = REF CodeBody; CodeBody: TYPE = RECORD[ text: Rope.ROPE, currentIndent: CARDINAL, context: TypeContextNode, variety: CodeVariety, codeName: NameNode, usage: Usage]; CodeVariety: TYPE = {def, impl}; BuildEmptyCode: PROC[context: TypeContextNode, name: NameNode, variety: CodeVariety] RETURNS[Code] = {RETURN[NEW[CodeBody_[NIL, 0, context, variety, name, BuildEmptyUsage[]]]]}; GetContextOfCode: PROC[code: Code] RETURNS[TypeContextNode] = {RETURN[code.context]}; AppendOpenRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "\N"]; FOR I: CARDINAL IN [0..code.currentIndent) DO IO.PutF[s, " "] ENDLOOP; IO.PutF[s, "%g", IO.rope[statement]]; code.currentIndent _ code.currentIndent+3; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; END; AppendRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "\N"]; FOR I: CARDINAL IN [0..code.currentIndent) DO IO.PutF[s, " "] ENDLOOP; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; END; ExtendOpenRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; code.currentIndent _ code.currentIndent+3; END; ExtendRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; END; AppendClosingRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "\N"]; FOR I: CARDINAL IN [0..code.currentIndent) DO IO.PutF[s, " "] ENDLOOP; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; code.currentIndent _ code.currentIndent-3; END; AppendClosingOpenRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "\N"]; FOR I: CARDINAL IN [0..code.currentIndent-2) DO IO.PutF[s, " "] ENDLOOP; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; END; ExtendClosingRopeCodeStatement: PROC[code: Code, statement: Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; IO.PutF[s, "%g", IO.rope[statement]]; code.text _ Rope.Cat[code.text, IO.RopeFromROS[s]]; code.currentIndent _ code.currentIndent - 3; END; RecordFunctionCall: PROC[code: Code, gContext: GeneratorContext, fnName: NameNode] = BEGIN defFile: NameNode; implFile: NameNode; [defFile, implFile] _ GetFunctionInfo[gContext, fnName]; IF EqualNames[implFile, code.codeName] THEN RETURN; RecordNameUse[code.usage, fnName, defFile, import] END; RecordFunctionExport: PROC[code: Code, gContext: GeneratorContext, fnName: NameNode] = {RecordNameUse[code.usage, fnName, GetFunctionInfo[gContext, fnName].defFile, export]}; RecordNamedTypeUse: PROC[code: Code, type: TypeNode] = BEGIN defFileName: NameNode _ GetTypeNodeTypeDefFile[type]; IF EqualNames[code.codeName, defFileName] THEN RETURN; IF defFileName = NIL THEN RETURN; RecordNameUse[code.usage, GetTypeNodeName[type], defFileName, ref]; END; RecordCedarTypeUse: PROC[code: Code, type: Rope.ROPE, from: Rope.ROPE] = BEGIN typeName: NameNode _ BuildRopeName[type]; defFileName: NameNode _ BuildRopeName[from]; RecordNameUse[code.usage, typeName, defFileName, ref]; END; RecordCedarProcUse: PROC[code: Code, proc: Rope.ROPE, from: Rope.ROPE] = BEGIN procName: NameNode _ BuildRopeName[proc]; defFileName: NameNode _ BuildRopeName[from]; RecordNameUse[code.usage, procName, defFileName, import]; END; ShowCode: PUBLIC PROC[code: Code, on: IO.STREAM] = BEGIN IO.PutF[on, "%g", IO.rope[BuildDirectoryRope[code.usage]]]; IO.PutF[on, "%g: %g%g%g =\N", IO.rope[GetNameNodeInfo[code.codeName].text], IO.rope[IF code.variety = def THEN "CEDAR DEFINITIONS" ELSE "CEDAR PROGRAM"], IO.rope[BuildImportsRope[code.usage]], IO.rope[BuildExportsRope[code.usage]]]; IO.PutF[on, "BEGIN\N"]; IO.PutF[on, "%g", IO.rope[BuildOpenRope[code.usage]]]; IO.PutF[on, "%g", IO.rope[code.text]]; IO.PutF[on, "\N\NEND.."]; END; -- usage info Use: TYPE = {ref, import, export}; Usage: TYPE = REF UsageBody; UsageBody: TYPE = RECORD[ ht: HashTable]; UseInfo: TYPE = REF UseInfoBody; UseInfoBody: TYPE = RECORD[ bits: ARRAY Use OF BOOLEAN, names: HashTable]; NameInfo: TYPE = REF CARDINAL; -- just for non nil fill BuildEmptyUsage: PROC[] RETURNS[Usage] = {RETURN[NEW[UsageBody_[CreateHashTable[10]]]]}; RecordNameUse: PROC[usage: Usage, name: NameNode, fromFile: NameNode, use: Use] = BEGIN info: UseInfo _ NARROW[FindEntry[usage.ht, fromFile]]; IF info = NIL THEN BEGIN info _ NEW[UseInfoBody_[[FALSE, FALSE, FALSE], CreateHashTable[10]]]; MakeEntry[usage.ht, fromFile, info]; END; info.bits[use] _ TRUE; SELECT use FROM export => NULL; ref, import => BEGIN nameInfo: NameInfo _ NARROW[FindEntry[info.names, name]]; IF nameInfo = NIL THEN MakeEntry[info.names, name, NEW[CARDINAL_0]]; END; ENDCASE => ERROR; END; BuildDirectoryRope: PROC[usage: Usage] RETURNS[Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; empty: BOOLEAN _ TRUE; SeeOneFileEntry: PROC[useRef: REF ANY, fileName: NameNode] = BEGIN useInfo: UseInfo _ NARROW[useRef]; noNames: BOOLEAN _ TRUE; SeeOneNameEntry: PROC[nameRef: REF ANY, name: NameNode] = BEGIN IF noNames THEN noNames _ FALSE ELSE IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[GetNameNodeInfo[name].text]]; END; IF empty THEN {empty _ FALSE; IO.PutF[s, "DIRECTORY\N"]} ELSE IO.PutF[s, ",\N"]; IO.PutF[s, " %g USING[", IO.rope[GetNameNodeInfo[fileName].text]]; EnumerateHashTable[useInfo.names, SeeOneNameEntry]; IO.PutF[s, "]"]; END; EnumerateHashTable[usage.ht, SeeOneFileEntry]; IF NOT empty THEN IO.PutF[s, ";\N\N"]; RETURN[IO.RopeFromROS[s]]; END; BuildImportsRope: PROC[usage: Usage] RETURNS[Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; empty: BOOLEAN _ TRUE; SeeOneFileEntry: PROC[useRef: REF ANY, fileName: NameNode] = BEGIN useInfo: UseInfo _ NARROW[useRef]; IF useInfo.bits[import] THEN BEGIN IF empty THEN {empty _ FALSE; IO.PutF[s, " IMPORTS "]} ELSE IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[GetNameNodeInfo[fileName].text]]; END; END; EnumerateHashTable[usage.ht, SeeOneFileEntry]; IF NOT empty THEN IO.PutF[s, " "]; RETURN[IO.RopeFromROS[s]]; END; BuildExportsRope: PROC[usage: Usage] RETURNS[Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; empty: BOOLEAN _ TRUE; SeeOneFileEntry: PROC[useRef: REF ANY, fileName: NameNode] = BEGIN useInfo: UseInfo _ NARROW[useRef]; IF useInfo.bits[export] THEN BEGIN IF empty THEN {empty _ FALSE; IO.PutF[s, " EXPORTS "]} ELSE IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[GetNameNodeInfo[fileName].text]]; END; END; EnumerateHashTable[usage.ht, SeeOneFileEntry]; IF NOT empty THEN IO.PutF[s, " "]; RETURN[IO.RopeFromROS[s]]; END; BuildOpenRope: PROC[usage: Usage] RETURNS[Rope.ROPE] = BEGIN s: IO.STREAM _ IO.ROS[]; empty: BOOLEAN _ TRUE; SeeOneFileEntry: PROC[useRef: REF ANY, fileName: NameNode] = BEGIN useInfo: UseInfo _ NARROW[useRef]; IF useInfo.bits[ref] OR useInfo.bits[import] THEN BEGIN IF empty THEN {empty _ FALSE; IO.PutF[s, "OPEN "]} ELSE IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[GetNameNodeInfo[fileName].text]]; END; END; EnumerateHashTable[usage.ht, SeeOneFileEntry]; IF NOT empty THEN IO.PutF[s, ";\N\N"]; RETURN[IO.RopeFromROS[s]]; END; END.. ΪA Comment is in order: why do I build a Cedar program to generate code, rather than generate code directly from the syntax, as is done in Casaba? Answer: For two reasons. First, I may generate type graphs from other sources, rather than the current textual source. e.g., from loaded Cedar programs. Second, I eventually want to do complicated things, for example, generate kipperers that have a different external interface from that of the binary data. This will be useful for data bases which have changing internal format, but we want to reflect a constant external format. Or, for remote procedure interface where one side is changing, but not the other. We begin with some code just to demonstrate that I understand the interface to type graphs (my own interface). Remark: December 11, 1985 9:58:46 am PST (Sturgis) basic marshaling has been working for a day, and I have decided to greatly simplify before adding the other features (e.g. lists) In the new mechanism, all code will be embedded in procedures with REF (or LIST) arguments or results. All intermediate expressions will either be the ref itself, or are to be treated as field specifications within the target record. (The ref is a field specification for the whole record.) This should GREATLY simplify the case analysis that occurs. For the moment, I will construct kipper and unkipper procedures for each named ref and each named record. The procedures constructed for the named records will assume ref argument or result. Later, I will put in a filter so that only one pair of these gets constructed for each record type. This describes an accompanying "exp" and an accompanying "Type" ref when expression is of type "REF Type" list when expression is of type "LIST Type" localVal for any local arg or variable that is "Type" shortField means its a field expression, for which @(expression) leads to a "SHORT POINTER TO Type" longField means its a field expression, for which @(expression) leads to a "LONG POINTER TO Type", or sometimes expression is a "LIST OF something". this is what happens for "exp.field" this procedure is used by the code that generates the refany kipper and unkipper procedures This procedure gets called by some generator, for example, to handle a field in a record When exp is placed in a right side environment it produces a value of type "type" the target type was not namable so, generate the expanded code procedure gets called by some generator, for example, to handle a field in a record When exp is placed in a left side environment it names a field of type "type" It is assumed that this is a field in a REF context, e.g. within a record known by a REF. the target type was not namable so, generate the expanded code same comments apply here as apply to KipperAType same comments apply here as apply to UnKipperAType I am currently using ORD and VAL. These are not expected to vary in Mimosa. It may be necessary at some future time to use arrays of values to handle compiler changes in the representation. See comments on KipperAnEnumType note that this takes the targetType as an argument first a one word flag, perhaps followed by more data 0 for body follows -- this agrees with Lupine "ref=NIL", when ref is non NIL. 1 for NIL -- this agrees with Lupine "ref=NIL", when ref is NIL. 2 for encoding as INT follows, assumes same ref was seen earlier note that this takes the targetType as an argument first a one word flag, perhaps followed by more data 0 for body follows -- this agrees with Lupine "ref=NIL", when ref is non NIL. 1 for NIL -- this agrees with Lupine "ref=NIL", when ref is NIL. 2 for encoding as INT follows, assumes same ref was seen earlier the target type had better have a name note that this takes the value type as an argument for each ref to a list cell first a one word flag, perhaps followed by more data this flag is designed to agree with Lupine "ref=NIL". 0 for body follows 1 for NIL note that this takes the value type as an argument the value type had better have a name assumes that exp is an expression of type CARDINAL assumes that exp is an expression of type INT. assumes that exp is an expression of type BOOLEAN. assumes that exp is an expression of type Rope.ROPE. fieldCase had better be ref Further, the work list mechanism has held REF ANY for last, so that ref types will be known, and can be seen via EnumerateHashTable[gContext.kipperedTypes] fieldCase had better be ref Further, the work list mechanism has held REF ANY for last, so that ref types will be known, and can be seen via EnumerateHashTable[gContext.kipperedTypes] assumes that exp is an expression of type CHAR assumes that exp is an expression of type INTEGER assumes that exp is an expression of type NAT assumes that exp is an expression of type REAL assumes that exp is an expression of type WORD assumes that exp is an expression of type Basics.CARD Here we have the actual kippering code that is dependent on the Kippering mechanism. For the moment, makes simple assumptions assumes doubleWordExp is an expression of type INT assumes arrayExp is of type LONG POINTER TO ARRAY [0..0) OF WORD, (note the old Mesa convention for an array of unknown size) and countExp is of type INT code manipulation Append means start a new line and install the current indent Extend means do not start a new line, or install the current indent Open means (after putting in argument rope) increment the current indent Closing means (after putting in argument rope) decrement the current rope ClosingOpen means install an indent 2 less than current Κ’˜Jšœ:˜:J˜*J˜˜ J˜J˜J˜6Jšœ˜Jšœ:˜:J˜%J˜J˜¬—J˜šœm˜mJšœ(˜(J˜™J™zJšœ…™…J™J™—J™nJ˜˜LJ˜˜"J˜J˜2J˜-J˜6J˜1J˜+J˜˜J˜J˜F—J˜J˜—J˜&J˜—J˜˜JJ˜J˜+˜J˜FJ˜R˜ J˜A—J˜VJ˜J˜[J˜$J˜—J˜—J˜˜JJ˜˜DJ˜J˜J˜9J˜&J˜—J˜%J˜—J˜˜7J˜J˜G˜$J˜9—˜#J˜9—J˜,J˜*J˜ J˜—J˜˜;J˜J˜˜"J˜J˜/J˜2J˜—J˜(J˜ J˜—J˜˜4J˜=—J˜J˜™2Jšœ™J™J™αJ™Jšœ€™€—J™J˜2˜$J˜J˜Jšœ˜J˜J˜J˜—J˜Jšœ4˜4šœ%˜%J˜šœ'˜'Jšœ˜Jšœ!˜!——J˜J˜,˜!J˜J˜—J˜J˜"˜J˜Jšœ˜J˜—J˜Jšœ4˜4Jšœ*˜*J˜˜B™?J™)J™+J™5J™cJ™”——J˜˜IJ˜˜J˜ J˜J˜'J˜—˜J˜——˜J™$—˜B˜J˜"J˜J˜J˜—J˜—J˜J˜˜WJ˜J˜7J˜˜.J˜J˜=J˜7J˜1J˜9J˜9Jšœ>˜>Jšœ@˜@J˜6J˜—J˜˜-J˜J˜;˜$J˜˜:J˜J˜+˜˜(J˜)—J˜J˜—J˜—J˜—Jšœ=˜=Jšœ?˜?J˜%J˜4J˜—J˜˜7J˜J˜+˜JšœB˜BJ˜,J˜—J˜—J˜˜˜>J˜'˜'J˜ J˜J˜J˜—J˜GJ˜Jšœb˜bJšœf˜fJ˜J˜4J˜6J˜Jšœ@˜@JšœE˜EJ˜J˜JšœC˜CJšœE˜EJ˜—J˜Jšœ…˜…J˜—˜Jšœ[™[—šœ€˜€J˜šœ8˜8J˜Jšœ'˜'šœ)˜)šœ2˜2˜JšœD˜DJšœH˜HJ˜——J˜—J˜—Jšœ?˜?J˜—J˜˜lJ˜J˜BJ˜-J˜—J˜˜tJ˜J˜EJ˜&J˜—J˜šœb˜bJ˜J˜F˜)J˜&J˜+J˜-J˜—˜)J˜J˜,J˜0J˜—J˜Jšœb˜bJ˜JšœJ˜JJšœN˜NJ˜J˜.J˜&J˜JšœF˜FJ˜@˜CJ˜—J˜,J˜˜.˜ Jšœ#˜#Jšœ˜J˜ —˜ J˜J˜J˜——˜.˜ Jšœ%˜%Jšœ*˜*J˜ —˜ J˜J˜J˜——J˜J˜/J˜˜3˜ Jšœ+˜+Jšœ"˜"J˜ —˜ J˜J˜J˜——J˜/J˜#J˜Jšœ[˜[J˜˜JJšœ&˜&J˜#Jšœ;˜;J˜—˜J˜˜JšœO˜OJšœI˜IJšœK˜KJ˜—J˜J˜—J˜5J˜˜)˜ ˜ J˜Jšœ%˜%Jšœ1˜1J˜ —˜ J˜J˜J˜———J˜/J˜#J˜Jšœ]˜]J˜˜JJ˜J˜#Jšœ=˜=J˜—˜J˜˜JšœQ˜QJšœK˜KJšœM˜MJ˜—J˜J˜—J˜5J˜J˜8J˜J˜&J˜'Jšœ;˜;Jšœ=˜=J˜J˜—J˜J˜J™J˜˜8J˜J˜J˜˜"J˜J˜TJ˜:J˜—J˜%J˜ J˜?J˜!J˜#J˜—J˜˜8J˜J˜+˜˜;J˜J˜4J˜QJ˜%J˜J˜eJ˜—˜ J˜J˜"˜,J˜˜˜>—˜ J˜J˜?J˜U˜:J˜JšœE˜EJ˜5˜Jšœ6˜6—J˜+J˜—˜AJ˜J˜3J˜3˜VJ˜ JšœP˜PJ˜5JšœT˜TJ˜—J˜˜J˜šœ*˜*J˜J˜J˜—JšœO˜OJ˜5JšœT˜TJ˜+J˜—˜J™J™Jšœ>˜>—J˜—J˜—J˜J˜—J˜J˜—˜Jšœ0™0—šœ~˜~J˜J˜+˜JšœJ˜JJšœL˜LJšœ>˜>JšœE˜EJšœ@˜@J˜ J˜—J˜—˜Jšœ2™2—šœ€˜€J˜J˜+˜JšœL˜LJšœN˜NJšœ@˜@JšœG˜GJšœB˜BJ˜ J˜—J˜—J˜šœv˜vJ˜J˜)J˜9˜IJ˜J˜;J˜JšœJ˜JJšœh˜hJ˜—J˜ J˜—J˜šœx˜xJ˜J˜)J˜9˜IJ˜J˜;J˜JšœL˜LJšœj˜jJ˜—J˜ J˜—J˜šœ˜J˜8J˜9J˜EJ˜PJ˜GJ˜5J˜0J˜˜)J˜3J˜JJšœ?˜?J˜1—J˜J˜—J˜šœ˜J˜J˜9J˜EJ˜PJ˜GJ˜5J˜0J˜˜)J˜3J˜JJšœA˜AJ˜1—J˜J˜J™—šœy˜yJ˜J™ΏJ˜/J˜EJšœ2˜2J˜—J˜šœ{˜{J˜Jšœ ™ J˜J˜/Jšœ4˜4J˜AJ˜—˜J™2—šœy˜yJ˜™4J™MJ™@J™@—J™J˜J˜/J˜JšœY˜YJ˜J˜:J˜˜`J˜ Jšœ9˜9JšœS˜SJ˜5JšœQ˜QJ˜—˜J˜J˜bJ˜šœZ˜ZJ˜'šœ'˜'J˜J˜iJ˜iJ˜KJ˜)J˜6J˜XJšœB˜BJ˜—Jšœ:˜:J˜-—Jšœ7˜7J˜—J˜J˜J™J™2—šœ{˜{J˜™4J™MJ™@J™@—J™J™&J™J™J˜/JšœY˜YJ˜J˜:J˜˜`J˜ Jšœ;˜;JšœU˜UJ˜5JšœT˜TJ˜—˜J˜J˜bJ˜iJšœU˜U˜˜>Jšœ5˜5Jšœ<˜˜=J˜J˜J˜˜.J˜˜'J˜.Jšœ4˜4—J˜J˜J˜—šœN˜NJ˜J˜@J˜‡J˜J˜0J˜GJ˜J˜„J˜—Jšœ ˜ Jšœ#˜#Jšœ1˜1J˜J˜:—Jšœ:˜:Jšœ=˜=J˜—˜J™.—šœd˜dJ˜J˜/J˜EJšœ/˜/J˜—J˜šœf˜fJ˜J˜/Jšœ2˜2J˜AJ˜—˜J™1—šœh˜hJšœ0˜0—J˜šœj˜jJšœ2˜2—˜J™-—šœc˜cJ˜J˜/J˜@Jšœ/˜/J˜—J˜šœe˜eJ˜J˜/Jšœ2˜2J˜