DIRECTORY IO, CrankTypes, MPTree, MPLeaves, Rope, CrankOps, CrankAnalyze; CrankTwoImpl: CEDAR PROGRAM IMPORTS IO, Rope, CrankOps, CrankAnalyze EXPORTS CrankAnalyze ~ BEGIN OPEN CrankTypes, CrankOps; ExternalProcType: PROC [typeGraph: TypeGraph, type: REF TypeRep.control] RETURNS [LIST OF REF] = { NamesFromFieldList: PROC [fieldList: FieldList] RETURNS [LIST OF REF] = { RETURN [IF fieldList = NIL THEN NIL ELSE CONS[fieldList.first.name, NamesFromFieldList[fieldList.rest]]] }; GetFieldNames: PROC [typeCode: TypeCode] RETURNS [LIST OF REF] = { WITH UnderType[typeGraph, typeCode] SELECT FROM r: REF TypeRep.record => RETURN [NamesFromFieldList[r.fieldList]]; ENDCASE => RETURN [NIL]; }; RETURN [LIST[GetFieldNames[type.argumentType], GetFieldNames[type.returnType]]] }; ParameterDefaults: PROC [typeGraph: TypeGraph, type: REF TypeRep.control] RETURNS [LIST OF REF] = { GetDefault: PROC [tc: TypeCode] RETURNS [Tree] = { DO WITH TypeFromTypeCode[typeGraph, tc] SELECT FROM t: REF TypeRep.definition => tc ¬ t.groundType; t: REF TypeRep.initial => RETURN [t.tree]; ENDCASE => RETURN [NIL]; ENDLOOP; }; DefaultsFromFieldList: PROC [fieldList: FieldList] RETURNS [LIST OF REF] = { RETURN [IF fieldList = NIL THEN NIL ELSE CONS[GetDefault[fieldList.first.rangeType], DefaultsFromFieldList[fieldList.rest]]] }; GetFieldDefaults: PROC [typeCode: TypeCode] RETURNS [LIST OF REF] = { WITH UnderType[typeGraph, typeCode] SELECT FROM r: REF TypeRep.record => RETURN [DefaultsFromFieldList[r.fieldList]]; ENDCASE => RETURN [NIL]; }; RETURN [GetFieldDefaults[type.argumentType]] }; FillTypeAttributes: PUBLIC PROC [typeGraph: TypeGraph, tree: Tree] = { WITH tree SELECT FROM aNode: AttributedNode => { WITH GetAttribute[aNode, $TYPECODE] SELECT FROM t: REF TypeCode => { type: Type = TypeFromTypeCode[typeGraph, t­]; IF type # NIL THEN AddAttribute[aNode, $TYPE, type.ext] }; ENDCASE => NULL; WITH GetAttribute[aNode, $DECLTYPECODE] SELECT FROM t: REF TypeCode => { type: Type = UnderType[typeGraph, t­]; IF type # NIL THEN { AddAttribute[aNode, $TYPECLASS, type.class] }; }; ENDCASE => NULL; FillTypeAttributes[typeGraph, aNode.syntaxNode]; }; node: REF MPTree.Node => { FOR i: NAT IN [1..node.sonLimit) DO FillTypeAttributes[typeGraph, node.son[i]]; ENDLOOP; }; ENDCASE => NULL; }; GetTypeQualifier: PROC [context: Context, typeCode: TypeCode, shortName: ROPE] RETURNS [qualifier: ROPE ¬ NIL] = { tc: TypeCode ¬ typeCode; DO WITH TypeFromTypeCode[context.types, tc] SELECT FROM d: REF TypeRep.definition => { IF Rope.Equal[d.shortName, shortName] THEN qualifier ¬ d.qualifier; tc ¬ d.groundType; }; ENDCASE => EXIT; ENDLOOP; }; AnalyzeModule: PUBLIC PROC [context: Context, tree: Tree] = { M: PROC [directory, imports, exports, shares, locks, decl: REF] = { self: AttributedNode = NARROW[tree]; {Action: PROC [item: Tree] = { AddDirectoryItem[context, item]; }; DoList[directory, Action]}; IF shares # NIL THEN ProcessSharesList[context, shares]; IF imports # NIL THEN ProcessModuleList[context, imports, TRUE]; IF exports # NIL THEN ProcessModuleList[context, exports, FALSE]; Assert[AnalyzeDecl[context, decl, self, 1]]; Assert[AnalyzeDecl[context, decl, self, 2]]; Assert[AnalyzeDecl[context, decl, self, 3]]; PropagateAttribute[self, $TYPECODE, NARROW[decl], $DECLTYPECODE]; }; Assert[With6[tree, module, M]]; }; AnalyzeDecl: PUBLIC PROC [context: Context, tree: Tree, parent: AttributedNode, pass: [1..3]] RETURNS [BOOL] = { D: PROC [idNode, typeNode, valueNode: REF] = { self: AttributedNode = NARROW[tree]; Each: PROC [item: Tree] = { id: ROPE = GetIdentifier[item]; SELECT pass FROM 1 => { ste: REF SymbolTableEntryRep.other ¬ NEW[SymbolTableEntryRep.other]; SELECT context.scopeKind FROM globalDefs => ste.qualifier ¬ context.moduleName; globalImpl => { publicType: TypeCode ¬ nullTypeCode; IF GetAttribute[tree, $PUBLIC] # NIL THEN [ste.qualifier, publicType] ¬ FindExportee[context, id]; WITH UnderType[context.types, publicType] SELECT FROM t: REF TypeRep.control => AddAttribute[NARROW[typeNode], $PARAMETERDEFAULTS, ParameterDefaults[context.types, t]]; ENDCASE => NULL; IF publicType # nullTypeCode THEN AddTypeCodeAttribute[self, $PUBLICTYPECODE, publicType]; IF ste.qualifier = NIL THEN ste.qualifier ¬ context.moduleName; }; ENDCASE => NULL; QualifyIdentifierNode[item, ste.qualifier]; ste.typeCode ¬ nullTypeCode; ste.readonly ¬ GetAttribute[self, $CONSTANT]=$TRUE; AddSymbol[context, id, ste]; IF context.moduleName = NIL THEN context.moduleName ¬ id; }; 2 => { ste: REF SymbolTableEntryRep.other ¬ NARROW[LookupSymbol[context, id]]; ste.typeCode ¬ GetTypeValueAttribute[context.types, typeNode]; IF context.fieldListLast # NIL THEN { add: FieldList = LIST[[id, ste.typeCode]]; context.fieldListLast.rest ¬ add; context.fieldListLast ¬ add; }; }; ENDCASE => NULL; }; IF pass = 2 THEN Assert[AnalyzeTypePass[context, typeNode, 1], "AnalyzeTypePass(%g)", [cardinal[pass]], typeNode]; DoList[idNode, Each]; IF pass >= 2 THEN Assert[AnalyzeTypePass[context, typeNode, pass],"AnalyzeTypePass(%g)", [cardinal[pass]], typeNode]; IF pass = 3 THEN { typeCode: TypeCode = GetTypeValueAttribute[context.types, typeNode]; AddTypeCodeAttribute[self, $DECLTYPECODE, typeCode]; IF valueNode # NIL THEN { PropagateAttribute[valueNode, $NEEDTYPECODE, self, $DECLTYPECODE]; WITH UnderType[context.types, typeCode] SELECT FROM c: REF TypeRep.control => { IF c.class = $PROC AND GetNodeName[typeNode] # $PROCTC THEN { AddAttribute[NARROW[typeNode], $PROCTYPE, ExternalProcType[context.types, c]]; }; }; ENDCASE => NULL; CrankAnalyze.AnalyzeBodyOrExpression[context, valueNode, self, 1]; CrankAnalyze.AnalyzeBodyOrExpression[context, valueNode, self, 2]; CrankAnalyze.AnalyzeBodyOrExpression[context, valueNode, self, 3]; WITH TypeFromTypeCode[context.types, GetTypeCodeAttribute[self, $DECLTYPECODE]] SELECT FROM d: REF TypeRep.record => { IF d.class = $DEFINITIONS THEN { IF d.fieldList = NIL OR d.fieldList.first # [NIL, nullTypeCode] THEN { Assert[FALSE, "Bug"] } ELSE { d.fieldList ¬ d.fieldList.rest }; }; }; ENDCASE => NULL; }; }; }; RETURN[With3[tree, decl, D]]; }; AnalyzeTypeDecl: PUBLIC PROC [context: Context, tree: Tree, parent: AttributedNode, pass: [1..3]] RETURNS [BOOL] = { D: PROC [identifierNode, typeNode, initialValueNode: REF] = { self: AttributedNode = NARROW[tree]; Each: PROC [item: Tree] = { id: ROPE = GetIdentifier[item]; SELECT pass FROM 1 => { typeCode: TypeCode = NewDefinitionType[typeGraph: context.types, qualifier: context.moduleName, shortName: id, groundType: nullTypeCode]; typeTypeCode: TypeCode = NewTypeType[context.types, typeCode]; ste: REF SymbolTableEntryRep.other = NEW[SymbolTableEntryRep.other ¬ [other[typeCode: typeTypeCode, readonly: TRUE]]]; IF context.scopeKind = globalImpl AND GetAttribute[tree, $PUBLIC] # NIL THEN { ste.qualifier ¬ TryExportingType[context, id, typeCode]; }; AddSymbol[context, id, ste]; }; 2 => { ste: REF SymbolTableEntryRep.other = NARROW[LookupSymbol[context, id]]; typeType: REF TypeRep.type = NARROW[TypeFromTypeCode[context.types, ste.typeCode]]; type: REF TypeRep.definition = NARROW[TypeFromTypeCode[context.types, typeType.value]]; type.groundType ¬ GetTypeValueAttribute[context.types, typeNode]; IF initialValueNode # NIL THEN { type.groundType ¬ NewInitialType[context.types, context.moduleName, initialValueNode, type.groundType]; }; IF ste.qualifier = NIL THEN { ste.qualifier ¬ GetTypeQualifier[context, type.typeCode, id]; }; QualifyIdentifierNode[item, ste.qualifier]; IF context.fieldListLast # NIL THEN { add: FieldList = LIST[[id, typeType.typeCode]]; context.fieldListLast.rest ¬ add; context.fieldListLast ¬ add; }; }; ENDCASE => NULL; }; IF pass = 2 THEN Assert[AnalyzeTypePass[context, typeNode, 1]]; IF pass <= 2 THEN DoList[identifierNode, Each]; IF pass = 2 THEN Assert[AnalyzeTypePass[context, typeNode, 2]]; IF pass = 3 THEN AnalyzeInitialValue[context, typeNode, initialValueNode]; }; RETURN[With3[tree, typedecl, D]]; }; FieldListFromEnumeration: PUBLIC PROC [items: LIST OF EnumerationItem, rangeType: TypeCode] RETURNS [FieldList] = { RETURN [ IF items = NIL THEN NIL ELSE CONS[[name: items.first.name, rangeType: rangeType], FieldListFromEnumeration[items.rest, rangeType]] ] }; AnalyzeFieldList: PUBLIC PROC [context: Context, fieldListNode: Tree] RETURNS [FieldList] = { fieldList: FieldList ¬ NIL; last: FieldList ¬ NIL; Append: PROC [f: FieldListItem] = { IF last = NIL THEN last ¬ fieldList ¬ LIST[f] ELSE {last.rest ¬ LIST[f]; last ¬ last.rest}; }; Each: PROC [fieldListEntryNode: Tree] = { FieldDecl: PROC [idsNode, typeNode, valueNode: Tree] = { EachId: PROC [fieldNameNode: Tree] = { id: ROPE ¬ NIL; rangeType: TypeCode ¬ nullTypeCode; Item: PROC [idNode, constantNode: Tree] = { id ¬ IF idNode # NIL THEN GetIdentifier[idNode] ELSE NIL; }; IF NOT With2[fieldNameNode, item, Item] AND fieldNameNode # NIL THEN id ¬ GetIdentifier[fieldNameNode]; rangeType ¬ GetTypeValueAttribute[context.types, typeNode]; IF valueNode # NIL THEN { rangeType ¬ NewInitialType[context.types, context.moduleName, valueNode, rangeType]; }; Append[[name: id, rangeType: rangeType]]; }; AnalyzeType2[context, typeNode]; AddTypeCodeAttribute[fieldListEntryNode, $DECLTYPECODE, GetTypeValueAttribute[context.types, typeNode]]; IF idsNode = NIL THEN Append[[name: NIL, rangeType: GetTypeValueAttribute[context.types, typeNode]]] ELSE DoList[idsNode, EachId]; }; Assert[With3[fieldListEntryNode, decl, FieldDecl]]; }; DoList[fieldListNode, Each]; RETURN [fieldList] }; AnalyzeFieldListValues: PROC [context: Context, fieldListNode: Tree] = { Each: PROC [fieldListEntryNode: Tree] = { FieldDecl: PROC [idsNode, typeNode, valueNode: Tree] = { AnalyzeInitialValue[context, typeNode, valueNode]; }; Assert[With3[fieldListEntryNode, decl, FieldDecl]]; }; DoList[fieldListNode, Each]; }; AnalyzeVariantList: PUBLIC PROC [context: Context, variantListNode: Tree] RETURNS [VariantList] = { variantList: VariantList ¬ NIL; last: VariantList ¬ NIL; Append: PROC [f: VariantListItem] = { IF last = NIL THEN last ¬ variantList ¬ LIST[f] ELSE {last.rest ¬ LIST[f]; last ¬ last.rest}; }; Each: PROC [variantListEntryNode: Tree] = { VariantDecl: PROC [idsNode, typeNode, valueNode: Tree] = { fieldList: FieldList ¬ NIL; VariantTC: PROC [fieldListNode: Tree] = { fieldList ¬ AnalyzeFieldList[context, fieldListNode]; }; EachId: PROC [idNode: Tree] = { tagId: ROPE = GetIdentifier[idNode]; Append[[value: tagId, chooses: fieldList]]; }; Assert[With1[typeNode, variantTC, VariantTC]]; Assert[valueNode=NIL, "initial value on variant part ignored",,variantListEntryNode]; DoList[idsNode, EachId]; }; Assert[With3[variantListEntryNode, typedecl, VariantDecl]]; }; DoList[variantListNode, Each]; RETURN [variantList] }; AnalyzeVariantListValues: PUBLIC PROC [context: Context, variantListNode: Tree] = { Each: PROC [variantListEntryNode: Tree] = { VariantDecl: PROC [idsNode, typeNode, valueNode: Tree] = { VariantTC: PROC [fieldListNode: Tree] = { AnalyzeFieldListValues[context, fieldListNode]; }; Assert[With1[typeNode, variantTC, VariantTC]]; Assert[valueNode=NIL, "Initial value on variant part ignored",,variantListEntryNode]; }; Assert[With3[variantListEntryNode, typedecl, VariantDecl]]; }; DoList[variantListNode, Each]; }; AnalyzeInitialValue: PROC [context: Context, typeNode, valueNode: REF] = { [] ¬ AnalyzeTypePass[context, typeNode, 3]; IF valueNode # NIL THEN { typeCode: TypeCode = GetTypeValueAttribute[context.types, typeNode]; AddTypeCodeAttribute[valueNode, $NEEDTYPECODE, typeCode]; WITH UnderType[context.types, typeCode] SELECT FROM c: REF TypeRep.control => NULL; ENDCASE => { Assert[CrankAnalyze.AnalyzeExpression[context, valueNode],"Expr expected",,valueNode]; }; }; }; GetLink: SIGNAL RETURNS [TypeCode] = CODE; -- for linkTC nested in listTC; sorry about this AnalyzeType: PUBLIC PROC [context: Context, tree: Tree] = { Assert[AnalyzeTypePass[context, tree, 1],"Need type",,tree]; [] ¬ AnalyzeTypePass[context, tree, 2]; [] ¬ AnalyzeTypePass[context, tree, 3]; }; AnalyzeType2: PROC [context: Context, tree: Tree] = { Assert[AnalyzeTypePass[context, tree, 1],"Need type",,tree]; [] ¬ AnalyzeTypePass[context, tree, 2]; }; AnalyzeTypePass: PUBLIC PROC [context: Context, tree: Tree, pass: [1..3]] RETURNS [BOOL] = { self: AttributedNode = NARROW[tree]; IndicateTypeCode: PROC [typeCode: TypeCode] = { AddTypeCodeAttribute[self, $TYPECODE, NewTypeType[context.types, typeCode]]; }; IndicateType: PROC [type: Type] = { IndicateTypeCode[NewType[context.types, type]]; }; ReferenceTypes: PROC RETURNS [BOOL] = { ReferenceTC: PROC [referentTypeNode: REF] = { SELECT pass FROM 1 => { IndicateTypeCode[NewReferenceType[context.types, nullTypeCode, SELECT GetNodeName[self] FROM $REFTC => $REF, $POINTERTC => $POINTER, $LISTTC => $LIST, ENDCASE => NIL]]; }; 2 => { type: REF TypeRep.reference ¬ NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; AnalyzeType2[context, referentTypeNode ! GetLink => {RESUME[type.typeCode]} -- for linkTC nested in listTC; sorry about this ]; type.referentType ¬ GetTypeValueAttribute[context.types, referentTypeNode]; }; 3 => { [] ¬ AnalyzeTypePass[context, referentTypeNode, 3] }; ENDCASE; }; RelativeTC: PROC [baseTypeNode, referentTypeNode: REF] = { SELECT pass FROM 1 => { IndicateTypeCode[NewReferenceType[context.types, nullTypeCode, $RELATIVEPOINTER]]; }; 2 => { type: REF TypeRep.reference ¬ NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; AnalyzeType2[context, baseTypeNode]; AnalyzeType2[context, referentTypeNode]; type.referentType ¬ GetTypeValueAttribute[context.types, referentTypeNode]; }; 3 => { [] ¬ AnalyzeTypePass[context, baseTypeNode, 3]; [] ¬ AnalyzeTypePass[context, referentTypeNode, 3]; }; ENDCASE; }; RETURN [With1[self, refTC, ReferenceTC] OR With1[self, pointerTC, ReferenceTC] OR With1[self, listTC, ReferenceTC] OR With2[self, relativeTC, RelativeTC]]; }; LinkTC: PROC = { SELECT pass FROM 1 => { IndicateTypeCode[SIGNAL GetLink]; -- sorry about this }; ENDCASE; }; ZoneTC: PROC = { IF pass = 1 THEN IndicateType[NEW[TypeRep.simple ¬ [class: $ZONE, v: simple[]]]]; }; ArraydescTC: PROC [referentTypeNode: REF] = { IF pass = 1 THEN IndicateType[NEW[TypeRep.array ¬ [class: $ARRAYDESCRIPTOR, v: array[domainType: nullTypeCode, rangeType: nullTypeCode]]]]; }; LongTC: PROC [referentTypeNode: REF] = { Assert[AnalyzeTypePass[context, referentTypeNode, pass]]; IF pass = 1 THEN { referentTypeCode: TypeCode = GetTypeValueAttribute[context.types, referentTypeNode]; referentType: REF TypeRep = UnderType[context.types, referentTypeCode]; type: REF TypeRep ¬ NIL; WITH referentType SELECT FROM t: REF TypeRep.array => { IF GetNodeName[referentTypeNode] = $ARRAYDESCTC THEN { type ¬ t; type.class ¬ $LONGARRAYDESCRIPTOR }; }; t: REF TypeRep.reference => { SELECT t.class FROM $POINTER => IF GetNodeName[referentTypeNode] = $POINTERTC THEN { type ¬ t; type.class ¬ $LONGPOINTER }; $REF, $LIST => { type ¬ referentType }; ENDCASE => NULL; }; t: REF TypeRep.scalar => { SELECT t.class FROM $INT16 => { type ¬ UnderType[context.types, LookupTypeCode[Root[context], "INT32"]] }; $CARD16 => { type ¬ UnderType[context.types, LookupTypeCode[Root[context], "CARD32"]] }; ENDCASE => NULL; }; ENDCASE => NULL; Assert[type # NIL, "LONG unimplemented in this context",,self]; IF type = NIL THEN RETURN; IF type = referentType THEN IndicateTypeCode[referentTypeCode] ELSE IndicateType[type]; }; }; ArrayTC: PROC [domainTypeNode, rangeTypeNode: REF] = { SELECT pass FROM 1 => { type: REF TypeRep.array = NEW[TypeRep.array ¬ [class: $ARRAY, v: array[domainType: nullTypeCode, rangeType: nullTypeCode]]]; IndicateType[type]; }; 2 => { type: REF TypeRep.array = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; AnalyzeType2[context, domainTypeNode]; AnalyzeType2[context, rangeTypeNode]; type.domainType ¬ GetTypeValueAttribute[context.types, domainTypeNode]; type.rangeType ¬ GetTypeValueAttribute[context.types, rangeTypeNode]; AddTypeCodeAttribute[rangeTypeNode, $DECLTYPECODE, type.rangeType]; }; 3 => { [] ¬ AnalyzeTypePass[context, domainTypeNode, 3]; [] ¬ AnalyzeTypePass[context, rangeTypeNode, 3]; }; ENDCASE; }; SubrangeTC: PROC [groundTypeNode, intervalNode: REF] = { SELECT pass FROM 1 => { type: REF TypeRep.subrange = NEW[TypeRep.subrange]; type.class ¬ $SUBRANGE; IndicateType[type]; }; 2 => { type: REF TypeRep.subrange = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; first: DINT ¬ DINT.FIRST; last: DINT ¬ DINT.LAST; groundTypeCode: TypeCode ¬ nullTypeCode; Interval: PROC [lowNode, highNode: REF] = { AddTypeCodeAttribute[lowNode, $NEEDTYPECODE, groundTypeCode]; PropagateAttribute[highNode, $NEEDTYPECODE, NARROW[lowNode], $NEEDTYPECODE]; Assert[CrankAnalyze.AnalyzeExpression[context, lowNode],"low not an expr",,self]; Assert[CrankAnalyze.AnalyzeExpression[context, highNode],"high not an expr",,self]; first ¬ GetSimpleConstant[context, lowNode ! Error => CONTINUE]; last ¬ GetSimpleConstant[context, highNode ! Error => CONTINUE]; }; RefFromInt: PROC [int: DINT] RETURNS [REF] ~ { SELECT int FROM IN DINT[INT.FIRST..INT.LAST] => RETURN [NEW[INT ¬ int]]; IN DINT[CARD.FIRST..CARD.LAST] => RETURN [NEW[CARD ¬ int]]; ENDCASE => RETURN [NEW[DINT ¬ int]]; }; AnalyzeType2[context, groundTypeNode]; groundTypeCode ¬ GetTypeValueAttribute[context.types, groundTypeNode]; SELECT TRUE FROM With2[intervalNode, intOO, Interval] => { first ¬ first+1; last ¬ last-1 }; With2[intervalNode, intCO, Interval] => { last ¬ last-1 }; With2[intervalNode, intOC, Interval] => { first ¬ first+1 }; With2[intervalNode, intCC, Interval] => NULL; ENDCASE => Assert[FALSE]; type.groundType ¬ groundTypeCode; type.first ¬ RefFromInt[first]; type.last ¬ RefFromInt[last]; }; 3 => { [] ¬ AnalyzeTypePass[context, groundTypeNode, 3] }; ENDCASE; }; DefinitionTC: PROC = { SELECT pass FROM 1 => { type: REF TypeRep.record = NEW[TypeRep.record ¬ [class: $DEFINITIONS, v: record[fieldList: NIL]]]; IndicateType[type]; }; ENDCASE; }; ControlTC: PROC [domainNode, rangeNode: REF] = { class: TypeClass ¬ SELECT GetNodeName[self] FROM $PROCTC => $PROC, $SIGNALTC => $SIGNAL, $ERRORTC => $ERROR, $PROGRAMTC => $PROGRAM, $PROCESSTC => $PROCESS, ENDCASE => ERROR; SELECT pass FROM 1 => { type: REF TypeRep.control = NEW[TypeRep.control ¬ [class: class, v: control[argumentType: nullTypeCode, returnType: nullTypeCode]]]; IndicateType[type]; }; 2 => { type: REF TypeRep.control = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; TypeCodeOf: PROC [listNode: REF] RETURNS [TypeCode] = { type: Type = IF listNode # NIL AND GetNodeName[listNode] = $ANYTC THEN NEW[TypeRep.simple ¬ [class: $ANY, v: simple[]]] ELSE NEW[TypeRep.record ¬ [class: $STRUCTURE, v: record[fieldList: AnalyzeFieldList[context, listNode]]]]; RETURN [NewType[context.types, type]] }; type.argumentType ¬ TypeCodeOf[domainNode]; type.returnType ¬ TypeCodeOf[rangeNode]; }; 3 => { type: REF TypeRep.control = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; WITH UnderType[context.types, type.argumentType] SELECT FROM s: REF TypeRep.record => {AnalyzeFieldListValues[context, domainNode]}; ENDCASE => NULL; WITH UnderType[context.types, type.returnType] SELECT FROM s: REF TypeRep.record => {AnalyzeFieldListValues[context, rangeNode]}; ENDCASE => NULL; }; ENDCASE; }; RecordTC: PROC [fieldListNode: REF] = { SELECT pass FROM 1 => { type: REF TypeRep.record = NEW[TypeRep.record ¬ [class: $RECORD, v: record[fieldList: NIL]]]; IndicateType[type]; }; 2 => { type: REF TypeRep.record = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; type.fieldList ¬ AnalyzeFieldList[context, fieldListNode]; }; 3 => { AnalyzeFieldListValues[context, fieldListNode] }; ENDCASE; }; UnionTC: PROC [tagDeclNode, variantsNode: REF] = { SELECT pass FROM 1 => { type: REF TypeRep.union = NEW[TypeRep.union]; type.class ¬ $UNION; IndicateType[type]; }; 2 => { type: REF TypeRep.union = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; TagDecl: PROC [tagIdNode, typeNode, valueNode: Tree] = { ImplicitTC: PROC = { Count: PROC [v: VariantList] RETURNS [i: INT ¬ 0] = INLINE {WHILE v # NIL DO i ¬ i + 1; v ¬ v.rest ENDLOOP}; n: INT = Count[type.variantList]; tagType: REF TypeRep.enumerated = NEW[TypeRep.enumerated ¬ [class: $ENUMERATION, v: enumerated[items: NIL]]]; last: LIST OF EnumerationItem ¬ NIL; i: INT ¬ 0; FOR each: VariantList ¬ type.variantList, each.rest UNTIL each = NIL DO new: LIST OF EnumerationItem ¬ LIST[[NARROW[each.first.value], i]]; IF last = NIL THEN tagType.items ¬ new ELSE last.rest ¬ new; last ¬ new; i ¬ i + 1; ENDLOOP; type.tagType ¬ NewType[context.types, tagType]; }; Item: PROC [idNode, constantNode: Tree] = { type.tagName ¬ IF idNode # NIL THEN GetIdentifier[idNode] ELSE NIL; }; IF tagIdNode # NIL AND NOT With2[tagIdNode, item, Item] THEN type.tagName ¬ GetIdentifier[tagIdNode]; Assert[valueNode = NIL, "Initializer not allowed on tag decl",,tree]; IF NOT With0[typeNode, implicitTC, ImplicitTC] THEN { AnalyzeType2[context, typeNode]; type.tagType ¬ GetTypeValueAttribute[context.types, typeNode]; }; }; type.variantList ¬ AnalyzeVariantList[context, variantsNode]; Assert[With3[tagDeclNode, decl, TagDecl]]; AddTypeCodeAttribute[tagDeclNode, $DECLTYPECODE, type.tagType]; }; 3 => { AnalyzeVariantListValues[context, variantsNode]; }; ENDCASE; }; SequenceTC: PROC [limitDeclNode, rangeTypeNode: Tree] = { SELECT pass FROM 1 => { type: REF TypeRep.sequence = NEW[TypeRep.sequence]; type.class ¬ $SEQUENCE; IndicateType[type]; }; 2 => { type: REF TypeRep.sequence = NARROW[TypeFromTypeCode[context.types, GetTypeValueAttribute[context.types, self]]]; LimitDecl: PROC [idNode, typeNode, valueNode: Tree] = { id: ROPE = IF idNode # NIL THEN GetIdentifier[idNode] ELSE NIL; AnalyzeType2[context, typeNode]; type.limitName ¬ id; type.domainType ¬ GetTypeValueAttribute[context.types, typeNode]; type.rangeType ¬ GetTypeValueAttribute[context.types, rangeTypeNode]; Assert[valueNode=NIL, "initial value on sequence limit ignored",, self]; }; AnalyzeType2[context, rangeTypeNode]; Assert[With3[limitDeclNode, decl, LimitDecl]]; AddTypeCodeAttribute[limitDeclNode, $DECLTYPECODE, type.rangeType]; }; 3 => { [] ¬ AnalyzeTypePass[context, rangeTypeNode, 3] }; ENDCASE; }; EnumeratedTC: PROC [idsNode: Tree] = { SELECT pass FROM 1 => { val: CARD ¬ 0; type: REF TypeRep.enumerated = NEW[TypeRep.enumerated]; last: LIST OF EnumerationItem ¬ NIL; Each: PROC [enumItem: Tree] = { id: ROPE ¬ NIL; new: LIST OF EnumerationItem ¬ NIL; Item: PROC [idNode, constantNode: Tree] = { newVal: CARD = GetSimpleConstant[context, constantNode]; id ¬ IF idNode # NIL THEN GetIdentifier[idNode] ELSE NIL; Assert[newVal >= val, "Enumeration out of order: %g", [cardinal[newVal]], idsNode]; val ¬ newVal; }; IF NOT With2[enumItem, item, Item] THEN id ¬ GetIdentifier[enumItem]; new ¬ LIST[[id, val]]; IF last = NIL THEN type.items ¬ new ELSE last.rest ¬ new; last ¬ new; val ¬ val + 1; }; type.class ¬ $ENUMERATION; val ¬ 0; DoList[idsNode, Each]; -- first pass determines range IndicateType[type]; }; 2 => { }; ENDCASE; }; VarTC: PROC [baseTypeNode: Tree] = { -- used for READONLY, VAR [] ¬ AnalyzeTypePass[context, baseTypeNode, pass]; IF GetAttribute[self, $TYPECODE] = NIL THEN { PropagateAttribute[self, $TYPECODE, NARROW[baseTypeNode], $TYPECODE]; }; }; AnyTC: PROC = { SELECT pass FROM 1 => { type: REF TypeRep.simple = NEW[TypeRep.simple]; type.class ¬ $ANY; IndicateType[type]; }; ENDCASE; }; OpaqueTC: PROC [REF] = { SELECT pass FROM 1 => { type: REF TypeRep.simple = NEW[TypeRep.simple]; type.class ¬ $OPAQUE; IndicateType[type]; }; ENDCASE; }; TypeExpression: PROC RETURNS [BOOL] = { SELECT pass FROM 1 => { IF self # NIL THEN { AddTypeCodeAttribute[self, $NEEDTYPECODE, 0]; -- 0 is predeclared to mean the right thing. RETURN [CrankAnalyze.AnalyzeExpression[context, self]]; }; RETURN [FALSE] }; 2, 3 => { IF pass = 3 THEN TypeExprPass3[context, self]; WITH UnderType[context.types, GetTypeCodeAttribute[self]] SELECT FROM t: REF TypeRep.type => RETURN [TRUE]; ENDCASE => RETURN [FALSE]; }; ENDCASE => ERROR; }; RETURN[With1[self, enumeratedTC, EnumeratedTC] OR With1[self, recordTC, RecordTC] OR With1[self, monitoredTC, RecordTC] OR ReferenceTypes[] OR With2[self, arrayTC, ArrayTC] OR With2[self, sequenceTC, SequenceTC] OR With2[self, procTC, ControlTC] OR With2[self, signalTC, ControlTC] OR With2[self, errorTC, ControlTC] OR With2[self, programTC, ControlTC] OR With2[self, processTC, ControlTC] OR With1[self, varTC, VarTC] OR With0[self, anyTC, AnyTC] OR With2[self, unionTC, UnionTC] OR With2[self, subrangeTC, SubrangeTC] OR With0[self, zoneTC, ZoneTC] OR With1[self, arraydescTC, ArraydescTC] OR With1[self, longTC, LongTC] OR With1[self, opaqueTC, OpaqueTC] OR With0[self, linkTC, LinkTC] OR With0[self, definitionTC, DefinitionTC] OR TypeExpression[]]; }; TypeExprPass3: PROC [context: Context, tree: Tree] = { self: AttributedNode = NARROW[tree]; Apply: PROC [operatorNode, operandListNode, catchNode: REF] = { SELECT self.syntaxNodeName FROM $DISCRIMINATE => TypeExprPass3[context, operatorNode]; $SEQUENCEALLOC => TypeExprPass3[context, operatorNode]; ENDCASE => Assert[FALSE,"What was this?",,self]; }; Dot: PROC [leftNode, rightNode: REF] = { SELECT self.syntaxNodeName FROM $DISCRIMINATE => TypeExprPass3[context, leftNode]; $INTERFACESELECT => NULL; ENDCASE => Assert[FALSE,"What was this?",,self]; }; Identifier: PROC [id: ROPE] = { IF GetAttribute[self, $QUAL] = NIL THEN { WITH LookupSymbol[context, id] SELECT FROM ste: REF SymbolTableEntryRep.other => { QualifyIdentifierNode[self, ste.qualifier] }; ENDCASE => Assert[FALSE,"What was this?",,self]; }; }; Assert[WithId[self, Identifier] OR With3[self, apply, Apply] OR With2[self, dot, Dot],"What was this?",,self]; }; FindExportee: PROC [context: Context, name: ROPE] RETURNS [to: ROPE ¬ NIL, tc: TypeCode ¬ nullTypeCode] = { matches: INT ¬ 0; FOR e: LIST OF Export ¬ context.exports, e.rest UNTIL e = NIL DO FOR f: FieldList ¬ e.first.fieldList, f.rest UNTIL f = NIL DO IF Rope.Equal[f.first.name, name] THEN { to ¬ e.first.interfaceName; tc ¬ f.first.rangeType; matches ¬ matches + 1; EXIT; }; ENDLOOP; ENDLOOP; IF matches # 1 THEN { [] ¬ Help[IO.PutFR["%g is exported to %g interfaces", [rope[name]], [integer[matches]]]]; }; }; TryExportingType: PROC [context: Context, name: ROPE, concrete: TypeCode] RETURNS [to: ROPE ¬ NIL] = { matches: INT ¬ 0; FOR each: LIST OF Export ¬ context.exports, each.rest UNTIL each = NIL DO FOR f: FieldList ¬ each.first.fieldList, f.rest UNTIL f = NIL DO IF Rope.Equal[f.first.name, name] THEN { WITH UnderType[context.types, f.first.rangeType] SELECT FROM t: REF TypeRep.type => { opaque: REF TypeRep = UnderType[context.types, t.value]; IF opaque.class = $OPAQUE THEN { ExportType[context: context, concrete: concrete, opaque: opaque.typeCode]; to ¬ each.first.interfaceName; matches ¬ matches + 1; EXIT; }; }; ENDCASE => NULL; }; ENDLOOP; ENDLOOP; IF matches # 1 THEN { [] ¬ Help[IO.PutFR["%g is exported to %g interfaces", [rope[name]], [integer[matches]]]]; }; }; ProcessModuleList: PUBLIC PROC [context: Context, moduleList: Tree, import: BOOL] = { Action: PROC [item: Tree] = { E: PROC [rename, name: REF] = { id: ROPE = GetIdentifier[name]; reid: ROPE = GetIdentifier[rename]; ste: REF SymbolTableEntryRep.directory ¬ NARROW[LookupSymbol[context, id]]; IF NOT Rope.Equal[reid, id] THEN { new: REF SymbolTableEntryRep.directory ¬ NEW[SymbolTableEntryRep.directory ¬ ste­]; AddSymbol[context, reid, new]; ste ¬ new; }; Assert[NOT (IF import THEN ste.import ELSE ste.export)]; -- check for duplicates IF import THEN ste.import ¬ TRUE ELSE ste.export ¬ TRUE; IF NOT import THEN { context.exports ¬ CONS[[id, ste.interfaceRecordType.fieldList], context.exports]; }; }; Assert[With2[item, item, E]]; }; DoList[moduleList, Action]; }; ProcessSharesList: PUBLIC PROC [context: Context, moduleList: Tree] = { Action: PROC [item: Tree] = { id: ROPE = GetIdentifier[item]; ste: REF SymbolTableEntryRep.directory ¬ NARROW[LookupSymbol[context, id]]; Assert[NOT ste.share]; ste.share ¬ TRUE; }; DoList[moduleList, Action]; }; AddDirectoryItem: PUBLIC PROC [context: Context, tree: Tree] = { D: PROC [directoryItem, from, usingList: REF] = { ste: REF SymbolTableEntryRep.directory ¬ NEW[SymbolTableEntryRep.directory]; directoryItemName: ROPE = GetIdentifier[directoryItem]; ste.import ¬ FALSE; ste.export ¬ FALSE; ste.from ¬ IF from = NIL THEN directoryItemName ELSE GetRopeLiteral[from]; ste.interfaceRecordType ¬ CrankAnalyze.GetSymbols[context, ste.from]; IF usingList = NIL THEN { ste.hasUsing ¬ FALSE; ste.using ¬ NIL } ELSE { list: LIST OF ROPE ¬ NIL; Action: PROC [item: Tree] = { list ¬ CONS[GetIdentifier[item], list] }; DoList[usingList, Action]; ste.hasUsing ¬ TRUE; ste.using ¬ list; }; AddSymbol[context, directoryItemName, ste]; }; Assert[With3[tree, diritem, D]]; }; END. ž CrankTwoImpl.mesa Copyright Σ 1987, 1991 by Xerox Corporation. All rights reserved. Michael Plass, March 23, 1993 10:56 am PST Type interpretation Semantic analysis Pass 1 defines the name in the context, with a null type code. Pass 2 assigns the type code, but with the type graph present only in skeleton form. Pass 3 analyzes the value. Supply attributes telling the names of the arguments and results remove the list head created by AnalyzeBodyOrExpression Pass 1 defines the name(s) in context, assigning a type code. Pass 2 fills in the type. Pass 3 analyzes the initial value. Ought to check for duplicates here. This is for the machine-dependent case. For now, we ignore constantNode, which may be a constant expression (the word offset) or an item whose first component is a word offset and whose second component is an item containing the first and last bit numbers. Ought to check for duplicates here. Pass 1 assigns the $TYPECODE attribute and creates an entry in the type graph. Pass 2 fills it in, assuming the other symbols in the context are at least partially defined Pass 3 analyzes the initial value of component types, and fills in qualifiers The following confused Dot interpretation, so we don't do it. IF GetNodeName[self] = $LISTTC THEN { WITH TypeFromTypeCode[context.types, type.referentType] SELECT FROM r: REF TypeRep.record => { -- Referents of LIST types are unpainted. It is OK to reach down and smash this, because the recordTC node was generated by the parser, and so nobody else can refer to it. r.class _ $STRUCTURE; }; ENDCASE => Assert[FALSE]; }; Should check for a base pointer. Will need to insert the symbols into the context of the body Need to cons up an enumerated type for type.tagType. type.variantList has been contructed already. This is for the machine-dependent case. For now, we ignore constantNode, which may be a constant expression (the word offset) or an item whose first component is a word offset and whose second component is an item containing the first and last bit numbers. Should check tags in type.variantList against type.tagType, and for duplicates. This is for the machine-dependent case All work has been done in the first pass. Κ{•NewlineDelimiter –(cedarcode) style™code™Kšœ Οeœ7™BK™*K™—KšΟk œžœ=˜IK˜KšΠln œž ˜Kšžœžœ˜(Kšžœ ˜Kšœžœžœ˜"head™šΟnœžœžœžœžœžœžœ˜bš  œžœžœžœžœžœ˜IKšžœžœ žœžœžœžœžœ;˜hK˜—š   œžœžœžœžœžœ˜Bšžœ žœž˜/Kšœžœžœ#˜BKšžœžœžœ˜—K˜—KšžœžœC˜OK˜K˜—š œžœžœžœžœžœžœ˜cš  œžœžœ ˜2šž˜šžœ!žœž˜0Kšœžœ)˜/Kšœžœžœ ˜*Kšžœžœžœ˜—Kšžœ˜—K˜—š  œžœžœžœžœžœ˜LKšžœžœ žœžœžœžœžœO˜|K˜—š  œžœžœžœžœžœ˜Ešžœ žœž˜/Kšœžœžœ&˜EKšžœžœžœ˜—K˜—Kšžœ&˜,K˜K˜—š œžœžœ'˜Fšžœžœž˜˜šžœ žœž˜/šœžœ˜K˜-Kšžœžœžœ%˜7K˜—Kšžœžœ˜—šžœ$žœž˜3šœžœ˜K˜&Kšžœžœžœ1˜CK˜—Kšžœžœ˜—K˜0K˜—šœžœ˜šžœžœžœž˜#K˜+Kšžœ˜—K˜—Kšžœžœ˜—K˜K˜—š  œžœ3žœžœ žœžœ˜rK˜šž˜šžœ%žœž˜4šœžœ˜Kšžœ$žœ˜CK˜K˜—Kšžœžœ˜—Kšžœ˜—K˜——™š  œžœžœ#˜=š œžœ4žœ˜CKšœžœ˜$šœ œžœ˜K˜ K˜—Kšžœ žœžœ$˜8Kšžœ žœžœ%žœ˜@Kšžœ žœžœ%žœ˜AK˜,K˜,K˜,Kšœ$žœ˜AK˜—K˜K˜K˜—š   œžœžœFžœžœ˜pK™>K™TK™š œžœžœ˜.Kšœžœ˜$š œžœ˜Kšœžœ˜šžœž˜˜Kšœžœžœ˜Dšžœž˜K˜1˜K˜$Kšžœžœžœ9˜bšžœ&žœž˜5Kšœžœ!žœE˜rKšžœžœ˜—Kšžœžœ9˜ZKšžœžœžœ$˜?K˜—Kšžœžœ˜—K˜+K˜K˜3K˜Kšžœžœžœ˜9K˜—˜Kšœžœžœ˜GK˜>šžœžœžœ˜%Kšœžœ˜*K˜!K˜K˜—K˜—Kšžœžœ˜—K˜—Kšžœ žœb˜rK˜Kšžœ žœd˜ušžœ žœ˜K˜DK˜4šžœ žœžœ˜K˜Bšžœ$žœž˜3šœžœ˜šžœžœ!žœ˜=K™@Kšœ žœ;˜NK˜—K˜—Kšžœžœ˜—K˜BK˜BK˜BšžœLžœž˜[šœžœ˜šžœžœ˜ Kšœ  ™7šžœžœžœžœ˜?Kšžœ žœ ˜Kšžœ$˜(—K˜—K˜—Kšžœžœ˜—K˜—K˜—K˜—Kšžœ˜K˜K˜—š  œžœžœFžœžœ˜tK™=K™K™"š œžœ.žœ˜=Kšœžœ˜$š œžœ˜Kšœžœ˜šžœž˜˜K˜‰K˜>KšœžœžœFžœ˜všžœ žœžœžœ˜NK˜8K˜—K˜K˜—˜Kšœžœžœ˜GKšœ žœžœ0˜SKšœžœžœ2˜WK˜Ašžœžœžœ˜ K˜gK˜—šžœžœžœ˜K˜=K˜—K˜+šžœžœžœ˜%Kšœžœ˜/K˜!K˜K˜—K˜—Kšžœžœ˜—K˜—Kšžœ žœ/˜?Kšžœ žœ˜/Kšžœ žœ/˜?Kšžœ žœ:˜JK˜—Kšžœ˜!K˜K˜—š  œžœžœ žœžœ'žœ˜sšžœ˜Kšžœ ž˜Kšžœžœž˜ Kšžœa˜eK˜—K˜K˜—š œžœžœ)žœ˜]Kšœžœ˜Kšœžœ˜š œžœ˜#K™#šžœž˜ Kšžœžœ˜Kšžœžœ˜-—K˜—š œžœ˜)š  œžœ)˜8š œžœ˜&Kšœžœžœ˜K˜#š œžœ!˜+K™'K™ΨKš œžœ žœžœžœžœ˜9K˜—Kš žœžœ"žœžœžœ#˜gK˜;šžœ žœžœ˜K˜TK˜—K˜)K˜—K˜ K˜hšžœ ž˜Kšžœžœ=˜SKšžœ˜—K˜—K˜3K˜—K˜Kšžœ ˜K˜K˜—š œžœ,˜Hš œžœ˜)š  œžœ)˜8K˜2K˜—K˜3K˜—K˜K˜K˜—š œžœžœ+žœ˜cKšœžœ˜Kšœžœ˜š œžœ˜%K™#šžœž˜ Kšžœžœ˜!Kšžœžœ˜-—K˜—š œžœ!˜+š  œžœ)˜:Kšœžœ˜š  œžœ˜)K˜5K˜—š œžœ˜Kšœžœ˜$K˜+K˜—K˜.KšœžœA˜UK˜K˜—K˜;K˜—K˜Kšžœ˜K˜K˜—š œžœžœ.˜Sš œžœ!˜+š  œžœ)˜:š  œžœ˜)K˜/K˜—K˜.KšœžœA˜UK˜—K˜;K˜—K˜K˜K˜—š œžœ)žœ˜JK˜+šžœ žœžœ˜K˜DK˜9šžœ$žœž˜3Kšœžœžœ˜šžœ˜ K˜VK˜——K˜—K˜K˜—š  œžœžœžœΟc0˜[K˜—š  œžœžœ#˜;K˜K˜—K˜—K˜=K˜*K˜?K™OK˜—˜K˜0K˜—Kšžœ˜—K˜—š  œžœ)˜9šžœž˜˜Kšœžœžœ˜3K˜K˜K˜—˜KšœžœžœN˜qš  œžœ(˜7Kš œžœžœ žœžœžœžœ˜?K˜ K˜K˜AK˜EKšœžœ4˜HK˜—K˜%K˜.K˜CK˜—K˜9Kšžœ˜—K˜—š  œžœ˜&šžœž˜˜Kšœžœ˜Kšœžœžœ˜7Kšœžœžœžœ˜$š œžœ˜Kšœžœžœ˜Kšœžœžœžœ˜#š œžœ!˜+K™&Kšœžœ,˜8Kš œžœ žœžœžœžœ˜9K˜SK˜ K˜—Kšžœžœžœ˜EKšœžœ ˜Kšžœžœžœžœ˜9K˜ K˜K˜—K˜Kšœ ‘˜>K˜K˜—˜K™)K˜—Kšžœ˜—K˜—š œžœ‘˜>Kšœ2˜2šžœ!žœžœ˜-Kšœ$žœ˜EKšœ˜—K˜—š œžœ˜šžœž˜˜Kšœžœžœ˜/K˜K˜K˜—Kšžœ˜—K˜—š œžœžœ˜šžœž˜˜Kšœžœžœ˜/K˜K˜K˜—Kšžœ˜—K˜—š œžœžœžœ˜'šžœž˜˜šžœžœžœ˜Kšœ.‘,˜ZKšžœ1˜7K˜—Kšžœžœ˜K˜—˜ Kšžœ žœ˜.šžœ6žœž˜EKšœžœžœžœ˜%Kšžœžœžœ˜—K˜—Kšžœžœ˜—K˜—Kš,žœ)žœ!žœ$žœžœžœ%žœ žœ"žœ!žœ#žœ#žœžœžœžœ%žœžœ'žœžœ!žœžœ)žœ˜ςK˜K˜—š  œžœ#˜6Kšœžœ˜$š œžœ,žœ˜?šžœž˜K˜6K˜7Kšžœ žœ˜0—K˜—š œžœžœ˜(šžœž˜K˜2Kšœžœ˜Kšžœ žœ˜0—K˜—š  œžœžœ˜šžœžœžœ˜)šžœžœž˜*KšœžœM˜UKšžœ žœ˜0—K˜—K˜—Kšœ žœžœ/˜nK˜K˜—š   œžœžœžœžœžœ"˜kK•StartOfExpansion= -- [key: ROPE, val: SymTab.Val] RETURNS [quit: BOOL _ FALSE]šœ žœ˜š žœžœžœ"žœžœž˜@šžœ*žœžœž˜=šžœ žœ˜(K˜K˜K˜Kšžœ˜K˜—Kšžœ˜—Kšžœ˜—šžœ žœ˜Kšœ žœM˜YK˜—K˜K˜—š  œžœžœžœžœžœ˜fK–= -- [key: ROPE, val: SymTab.Val] RETURNS [quit: BOOL _ FALSE]šœ žœ˜–= -- [key: ROPE, val: SymTab.Val] RETURNS [quit: BOOL _ FALSE]š žœžœžœ%žœžœž˜Išžœ-žœžœž˜@šžœ žœ˜(šžœ-žœž˜<šœžœ˜Kšœžœ-˜8šžœžœ˜ K˜JK˜K˜Kšžœ˜K˜—K˜—Kšžœžœ˜—K˜—Kšžœ˜—Kšžœ˜—šžœ žœ˜Kšœ žœM˜YK˜—K˜K˜—š œžœžœ.žœ˜Uš œžœ˜š œžœžœ˜Kšœžœ˜Kšœžœ˜#Kšœžœ!žœ˜Kšžœžœžœ˜"K–1[x: SymTab.Ref, key: ROPE, val: SymTab.Val]šœžœ!žœ'˜SK˜K˜ K˜—Kš œžœžœžœ žœ‘˜PKš žœžœžœžœžœ˜8šžœžœžœ˜Kšœžœ;˜QK˜—K˜—K˜K˜—K˜K˜K˜—š œžœžœ)˜Gš œžœ˜Kšœžœ˜Kšœžœ!žœ˜KKšœžœ ˜Kšœ žœ˜K˜—K˜K˜K˜—š œžœžœ#˜@š œžœ"žœ˜1Kšœžœ!žœ ˜LKšœžœ ˜7Kšœ žœ˜Kšœ žœ˜Kš œ žœžœžœžœ˜JK˜Ešžœ ž˜Kšžœžœžœ˜.šžœ˜Kš œžœžœžœžœ˜Kš œžœžœ˜GK˜Kšœžœ˜K˜K˜——K–1[x: SymTab.Ref, key: ROPE, val: SymTab.Val]˜+K˜—K˜ K˜K˜——Kšžœ˜K˜K˜K˜—…—q2™K