DIRECTORY A3: TYPE USING [ Bundling, CanonicalType, IdentifiedType, IndexType, MarkedType, NullableType, OperandInline, OrderedType, PermanentType, TargetType, TypeForTree, Unbundle], Alloc: TYPE USING [Notifier], ComData: TYPE USING [ idCARDINAL, idTEXT, interface, ownSymbols, typeATOM, typeAtomRecord, typeBOOL, typeCHAR, typeINT, typeREAL, typeSTRING], LiteralOps: TYPE USING [FindHeapString], Log: TYPE USING [Error, ErrorHti, ErrorN, ErrorNode, ErrorSei, ErrorTree, Warning], P3: TYPE USING [ Attr, emptyAttr, fullAttr, voidAttr, NarrowOp, NPUse, BoundNP, MergeNP, SequenceNP, TextForm, phraseNP, AddrOp, All, And, Apply, Assignment, Case, CatchPhrase, ClearRefStack, Discrimination, Dot, EnterType, Extract, Id, MakeLongType, MakeRefType, MiscXfer, Narrowing, Range, RecordMention, SealRefStack, SearchCtxList, TextRep, TypeAppl, TypeExp, UnsealRefStack], P3S: TYPE USING [ImplicitInfo, implicitRecord, safety, self], SymLiteralOps: TYPE USING [EnterAtom, EnterText], Symbols: TYPE USING [ Base, HTIndex, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CSENull, RecordSENull, codeANY, codeCHAR, codeINT, typeANY, ctxType, seType], SymbolOps: TYPE USING [ConstantId, NormalType, RCType, TypeForm, TypeRoot, UnderType], Tree: TYPE USING [Base, Index, Link, Map, Null, NullIndex, treeType], TreeOps: TYPE USING [ FreeNode, GetHash, GetNode, ListLength, PopTree, PushTree, PushNode, SetAttr, SetInfo, UpdateList], Types: TYPE USING [SymbolTableBase, Assignable, Equivalent]; Pass3Xb: PROGRAM IMPORTS A3, LiteralOps, Log, P3, P3S, SymLiteralOps, SymbolOps, TreeOps, Types, dataPtr: ComData EXPORTS P3, P3S = { OPEN SymbolOps, Symbols, TreeOps, A3, P3; tb: Tree.Base; -- tree base address (local copy) seb: Base; -- se table base address (local copy) ctxb: Base; -- context table base address (local copy) own: Types.SymbolTableBase; zone: UNCOUNTED ZONE _ NIL; ExpBNotify: PUBLIC Alloc.Notifier = { seb _ base[seType]; ctxb _ base[ctxType]; tb _ base[Tree.treeType]}; OperandDescriptor: TYPE = RECORD[ type: CSEIndex, -- type of operand attr: Attr]; -- attributes RStack: TYPE = RECORD [SEQUENCE length: NAT OF OperandDescriptor]; rStack: LONG POINTER TO RStack _ NIL; rI: INTEGER; -- index into rStack RPush: PUBLIC PROC [type: CSEIndex, attr: Attr] = { rI _ rI + 1; WHILE rI >= rStack.length DO newLength: NAT = rStack.length + 16; newStack: LONG POINTER TO RStack = zone.NEW[RStack[newLength]]; FOR i: INTEGER IN [0 .. rI) DO newStack[i] _ rStack[i] ENDLOOP; zone.FREE[@rStack]; rStack _ newStack; ENDLOOP; rStack[rI] _ [type:type, attr:attr]}; RPop: PUBLIC PROC = {IF rI < 0 THEN ERROR; rI _ rI-1}; RType: PUBLIC PROC RETURNS [CSEIndex] = {RETURN [rStack[rI].type]}; RAttr: PUBLIC PROC RETURNS [Attr] = {RETURN [rStack[rI].attr]}; longUnsigned: CSEIndex; -- a hint for mwconst textType: ARRAY TextForm OF CSEIndex; -- a hint for text literals ExpInit: PUBLIC PROC [scratchZone: UNCOUNTED ZONE] = { zone _ scratchZone; implicit _ [type: typeANY, tree: Tree.Null, attr: emptyAttr]; P3S.implicitRecord _ RecordSENull; own _ dataPtr.ownSymbols; -- make a parameter? longUnsigned _ CSENull; textType _ ALL[CSENull]; rStack _ zone.NEW[RStack[32]]; rI _ -1}; ExpReset: PUBLIC PROC = { IF rStack # NIL THEN zone.FREE[@rStack]; zone _ NIL}; EqualTypes: PROC [type1, type2: CSEIndex] RETURNS [BOOL] = { RETURN [Types.Equivalent[[own, type1], [own, type2]]]}; UnresolvedTypes: SIGNAL RETURNS [CSEIndex] = CODE; BalanceTypes: PROC [type1, type2: CSEIndex] RETURNS [type: CSEIndex] = { n1, n2: CARDINAL; SELECT TRUE FROM (type1 = type2), (type2 = typeANY) => type _ type1; (type1 = typeANY) => type _ type2; ENDCASE => { n1 _ Bundling[type1]; n2 _ Bundling[type2]; WHILE n1 > n2 DO type1 _ Unbundle[LOOPHOLE[type1]]; n1 _ n1-1 ENDLOOP; WHILE n2 > n1 DO type2 _ Unbundle[LOOPHOLE[type2]]; n2 _ n2-1 ENDLOOP; DO type1 _ TargetType[type1]; type2 _ TargetType[type2]; SELECT TRUE FROM Types.Assignable[[own, type1], [own, type2]] => {type _ type1; EXIT}; Types.Assignable[[own, type2], [own, type1]] => {type _ type2; EXIT}; ENDCASE; IF n1 = 0 THEN GO TO Fail; n1 _ n1-1; type1 _ Unbundle[LOOPHOLE[type1]]; type2 _ Unbundle[LOOPHOLE[type2]]; REPEAT Fail => type _ SIGNAL UnresolvedTypes; ENDLOOP}; RETURN}; ForceType: PUBLIC PROC [t: Tree.Link, type: CSEIndex] RETURNS [Tree.Link] = { PushTree[t]; IF t = Tree.Null THEN PushNode[cast, 1] ELSE WITH t SELECT FROM subtree => SELECT tb[index].name FROM construct, union, rowcons => PushNode[cast, 1]; openx => PushNode[cast, 1]; ENDCASE; ENDCASE => PushNode[cast, 1]; SetInfo[type]; RETURN [PopTree[]]}; UpArrow: PUBLIC PROC [node: Tree.Index] = { OPEN tb[node]; type, nType: CSEIndex; attr: Attr; son[1] _ Exp[son[1], typeANY]; type _ RType[]; attr _ RAttr[]; RPop[]; attr.const _ FALSE; DO nType _ NormalType[type]; WITH t: seb[nType] SELECT FROM ref => { RPush[UnderType[t.refType], attr]; attr2 _ seb[type].typeTag = long; IF P3S.safety = checked AND ~(t.counted OR PermanentType[t.refType]) THEN Log.ErrorNode[unsafeOperation, node]; EXIT}; record => { IF Bundling[nType] = 0 THEN GO TO fail; type _ Unbundle[LOOPHOLE[nType, RecordSEIndex]]}; ENDCASE => GO TO fail; REPEAT fail => { IF type # typeANY THEN Log.ErrorTree[typeClash, son[1]]; RPush[typeANY, attr]}; ENDLOOP}; MakeNumeric: PROC [type: CSEIndex] RETURNS [CSEIndex] = { RETURN [SELECT seb[type].typeTag FROM long => MakeLongType[dataPtr.typeINT, type], ENDCASE => dataPtr.typeINT]}; EvalNumeric: PROC [t: Tree.Link] RETURNS [val: Tree.Link] = { val _ GenericRhs[t, dataPtr.typeINT]; SELECT NormalType[rStack[rI].type] FROM dataPtr.typeINT => NULL; typeANY => rStack[rI].type _ MakeNumeric[rStack[rI].type]; ENDCASE => Log.ErrorTree[typeClash, val]; RETURN}; ArithOp: PROC [node: Tree.Index] = { OPEN tb[node]; saveNP: NPUse; son[1] _ EvalNumeric[son[1]]; saveNP _ phraseNP; son[2] _ EvalNumeric[son[2]]; BalanceAttributes[node]; rStack[rI-1].attr _ And[rStack[rI-1].attr, rStack[rI].attr]; IF attr1 THEN rStack[rI-1].attr.const _ FALSE; RPop[]; phraseNP _ MergeNP[saveNP][phraseNP]}; ArithType: PROC [type: CSEIndex] RETURNS [CSEIndex] = { type _ NormalType[type]; RETURN [WITH seb[type] SELECT FROM relative => NormalType[UnderType[offsetType]], ENDCASE => type]}; Plus: PROC [node: Tree.Index] = { OPEN tb[node]; type: CSEIndex; lr: BOOL; saveNP: NPUse; son[1] _ GenericRhs[son[1], typeANY]; saveNP _ phraseNP; type _ ArithType[rStack[rI].type]; IF seb[type].typeTag = ref OR type = dataPtr.typeCHAR THEN { IF RCType[type] # none THEN Log.ErrorTree[typeClash, son[1]]; lr _ TRUE; son[2] _ EvalNumeric[son[2]]} ELSE { SELECT type FROM dataPtr.typeINT, typeANY => NULL; ENDCASE => Log.ErrorTree[typeClash, son[1]]; son[2] _ GenericRhs[son[2], typeANY]; lr _ FALSE; type _ ArithType[rStack[rI].type]; SELECT TRUE FROM type = dataPtr.typeINT, type = dataPtr.typeCHAR => NULL; seb[type].typeTag = ref => IF RCType[type] # none THEN Log.ErrorTree[typeClash, son[2]]; ENDCASE => { IF type # typeANY THEN Log.ErrorTree[typeClash, son[2]]; rStack[rI].type _ MakeNumeric[rStack[rI].type]}}; IF P3S.safety = checked AND seb[type].typeTag = ref THEN Log.ErrorNode[unsafeOperation, node]; BalanceAttributes[node]; rStack[rI-1].attr _ And[rStack[rI-1].attr, rStack[rI].attr]; IF attr1 THEN rStack[rI-1].attr.const _ FALSE; IF ~lr THEN rStack[rI-1].type _ rStack[rI].type; RPop[]; phraseNP _ MergeNP[saveNP][phraseNP]}; Minus: PROC [node: Tree.Index] = { OPEN tb[node]; type, lType, rType: CSEIndex; lr: BOOL; saveNP: NPUse; son[1] _ GenericRhs[son[1], typeANY]; saveNP _ phraseNP; type _ NormalType[rStack[rI].type]; lType _ ArithType[type]; lr _ TRUE; IF seb[lType].typeTag = ref OR lType = dataPtr.typeCHAR THEN { IF RCType[lType] # none THEN Log.ErrorTree[typeClash, son[1]]; son[2] _ GenericRhs[son[2], typeANY]; rType _ ArithType[rStack[rI].type]; SELECT TRUE FROM rType = typeANY => NULL; Types.Equivalent[[own, lType], [own, rType]] => lr _ FALSE; rType = dataPtr.typeINT => NULL; ENDCASE => Log.ErrorTree[typeClash, son[2]]} ELSE { SELECT type FROM dataPtr.typeINT, typeANY => NULL; ENDCASE => {Log.ErrorTree[typeClash, son[1]]; rStack[rI].type _ typeANY}; son[2] _ EvalNumeric[son[2]]}; IF P3S.safety = checked AND seb[lType].typeTag = ref THEN Log.ErrorNode[unsafeOperation, node]; BalanceAttributes[node]; rStack[rI-1].attr _ And[rStack[rI-1].attr, rStack[rI].attr]; IF attr1 THEN rStack[rI-1].attr.const _ FALSE; IF ~lr THEN rStack[rI-1].type _ IF attr2 THEN MakeLongType[dataPtr.typeINT, rStack[rI].type] ELSE dataPtr.typeINT; RPop[]; phraseNP _ MergeNP[saveNP][phraseNP]}; UnaryOp: PROC [node: Tree.Index] = { IF UniOperand[node] THEN { tb[node].son[1] _ EvalNumeric[tb[node].son[1]]; SetAttributes[node]; IF tb[node].attr1 THEN rStack[rI].attr.const _ FALSE}}; EnumOp: PROC [node: Tree.Index, target: CSEIndex] = { IF UniOperand[node] THEN { tb[node].son[1] _ GenericRhs[tb[node].son[1], target]; SetAttributes[node]; IF ~IndexType[RType[]] THEN Log.ErrorTree[typeClash, tb[node].son[1]]}}; RelOp: PROC [node: Tree.Index, ordered: BOOL] = { OPEN tb[node]; type: CSEIndex; attr: Attr; saveNP: NPUse; implicitOp: BOOL; son[1] _ GenericRhs[son[1], typeANY]; saveNP _ phraseNP; type _ NormalType[RType[]]; attr _ RAttr[]; implicitOp _ (son[1] = Tree.Null); son[2] _ GenericRhs[son[2], type]; type _ BalanceTypes[type, NormalType[RType[]] ! UnresolvedTypes => {Log.ErrorTree[typeClash, son[2]]; RESUME [typeANY]}]; IF (ordered AND ~OrderedType[type]) OR (~ordered AND ~IdentifiedType[type]) THEN Log.ErrorNode[relationType, node]; BalanceAttributes[node]; attr _ And[attr, RAttr[]]; IF implicitOp AND son[1] # Tree.Null THEN Log.ErrorTree[typeClash, son[2]]; SELECT seb[type].typeTag FROM basic, enumerated => NULL; transfer => { IF OperandInline[son[1]] THEN Log.ErrorTree[misusedInline, son[1]]; IF OperandInline[son[2]] THEN Log.ErrorTree[misusedInline, son[2]]; attr.const _ FALSE}; real => attr.const _ FALSE; ENDCASE; RPop[]; RPop[]; RPush[dataPtr.typeBOOL, attr]; phraseNP _ MergeNP[saveNP][phraseNP]}; In: PROC [node: Tree.Index] = { OPEN tb[node]; type: CSEIndex; saveNP: NPUse; son[1] _ GenericRhs[son[1], typeANY]; saveNP _ phraseNP; type _ RType[]; son[2] _ Range[son[2], CanonicalType[type]]; [] _ BalanceTypes[NormalType[type], NormalType[RType[]] ! UnresolvedTypes => {Log.ErrorTree[typeClash, tb[node].son[1]]; RESUME [typeANY]}]; BalanceAttributes[node]; rStack[rI-1].attr _ And[rStack[rI-1].attr, rStack[rI].attr]; RPop[]; rStack[rI].type _ dataPtr.typeBOOL; phraseNP _ MergeNP[saveNP][phraseNP]}; BoolOp: PROC [node: Tree.Index] = { OPEN tb[node]; attr: Attr; saveNP: NPUse; SealRefStack[]; son[1] _ Rhs[son[1], dataPtr.typeBOOL]; attr _ RAttr[]; saveNP _ phraseNP; ClearRefStack[]; son[2] _ Rhs[son[2], dataPtr.typeBOOL]; UnsealRefStack[]; attr _ And[attr, RAttr[]]; RPop[]; RPop[]; RPush[dataPtr.typeBOOL, attr]; phraseNP _ SequenceNP[saveNP][phraseNP]}; Interval: PUBLIC PROC [t: Tree.Link, target: CSEIndex, constant: BOOL] = { node: Tree.Index = GetNode[t]; type: CSEIndex; attr: Attr; saveNP: NPUse; target _ TargetType[target]; tb[node].son[1] _ BalancedRhs[tb[node].son[1], target]; saveNP _ phraseNP; type _ rStack[rI].type _ CanonicalType[rStack[rI].type]; attr _ RAttr[]; IF constant AND ~attr.const THEN Log.ErrorTree[nonConstant, tb[node].son[1]]; tb[node].son[2] _ BalancedRhs[tb[node].son[2], target]; rStack[rI].type _ CanonicalType[rStack[rI].type]; [] _ BalanceTypes[NormalType[type], NormalType[RType[]] ! UnresolvedTypes => {Log.ErrorTree[typeClash, tb[node].son[2]]; RESUME [typeANY]}]; attr _ And[attr, RAttr[]]; IF constant AND ~RAttr[].const THEN Log.ErrorTree[nonConstant, tb[node].son[2]]; BalanceAttributes[node]; IF tb[node].attr1 THEN attr.const _ FALSE; phraseNP _ MergeNP[saveNP][phraseNP]; RPop[]; rStack[rI].attr _ attr}; BalancedTarget: PROC [target, type: CSEIndex] RETURNS [CSEIndex] = { RETURN [IF target = typeANY OR (~EqualTypes[type, target] AND NormalType[type] = target) THEN TargetType[type] ELSE target]}; ResolveTypes: PROC [type1, type2, target: CSEIndex, t: Tree.Link] RETURNS [type: CSEIndex] = { failed: BOOL; IF target = typeANY THEN failed _ TRUE ELSE { ENABLE UnresolvedTypes => {failed _ TRUE; RESUME [typeANY]}; failed _ FALSE; type1 _ BalanceTypes[target, type1]; type2 _ BalanceTypes[target, type2]; type _ BalanceTypes[type1, type2]}; IF failed THEN {Log.ErrorTree[typeClash, t]; type _ typeANY}; RETURN}; IfExp: PROC [node: Tree.Index, target: CSEIndex] = { OPEN tb[node]; type: CSEIndex; attr: Attr; entryNP, saveNP: NPUse; SealRefStack[]; son[1] _ Rhs[son[1], dataPtr.typeBOOL]; attr _ RAttr[]; RPop[]; entryNP _ phraseNP; UnsealRefStack[]; son[2] _ BalancedRhs[son[2], target]; attr _ And[attr, RAttr[]]; saveNP _ SequenceNP[entryNP][phraseNP]; type _ RType[]; RPop[]; target _ BalancedTarget[target, type]; son[3] _ BalancedRhs[son[3], target]; attr _ And[attr, RAttr[]]; type _ BalanceTypes[type, RType[] ! UnresolvedTypes => {RESUME [ResolveTypes[type, RType[], target, son[3]]]}]; IF seb[type].typeTag = transfer THEN { IF OperandInline[son[2]] THEN Log.ErrorTree[misusedInline, son[2]]; IF OperandInline[son[3]] THEN Log.ErrorTree[misusedInline, son[3]]; attr.const _ FALSE}; phraseNP _ BoundNP[saveNP][SequenceNP[entryNP][phraseNP]]; RPop[]; RPush[type, attr]}; SelectExp: PROC [ node: Tree.Index, target: CSEIndex, driver: PROC [Tree.Index, Tree.Map], foldable: BOOL] = { type: CSEIndex; attr: Attr; saveNP: NPUse; started: BOOL; Selection: Tree.Map = { subType: CSEIndex; entryNP: NPUse = phraseNP; v _ BalancedRhs[t, target]; subType _ BalanceTypes[type, RType[] ! UnresolvedTypes => {RESUME [ResolveTypes[type, RType[], target, v]]}]; IF seb[subType].typeTag = transfer AND OperandInline[v] THEN Log.ErrorTree[misusedInline, v]; saveNP _ BoundNP[saveNP][SequenceNP[entryNP][phraseNP]]; IF subType # typeANY THEN type _ subType; IF ~started THEN target _ BalancedTarget[target, type]; attr _ And[attr, RAttr[]]; RPop[]; started _ TRUE; RETURN}; type _ typeANY; attr _ fullAttr; started _ FALSE; saveNP _ none; driver[node, Selection]; attr _ And[attr, RAttr[]]; RPop[]; attr.const _ foldable AND attr.const AND tb[node].attr2; RPush[type, attr]; phraseNP _ saveNP}; MinMax: PROC [node: Tree.Index, target: CSEIndex] = { OPEN tb[node]; attr: Attr; saveNP: NPUse; started: BOOL; type: CSEIndex; SubMinMax: Tree.Map = { subType: CSEIndex; v _ BalancedRhs[t, target]; attr _ And[attr, RAttr[]]; saveNP _ MergeNP[saveNP][phraseNP]; subType _ CanonicalType[RType[]]; subType _ BalanceTypes[subType, type ! UnresolvedTypes => {RESUME[ResolveTypes[subType, type, target, v]]}]; IF type # subType AND subType # typeANY THEN { IF ~OrderedType[subType] THEN Log.ErrorNode[relationType, node]; type _ subType; IF ~started THEN target _ BalancedTarget[target, type]}; RPop[]; started _ TRUE; RETURN}; attr _ fullAttr; saveNP _ none; started _ FALSE; type _ typeANY; son[1] _ UpdateList[son[1], SubMinMax]; SELECT seb[type].typeTag FROM long => {attr1 _ FALSE; attr2 _ TRUE}; real => {attr1 _ TRUE; attr2 _ FALSE; attr.const _ FALSE}; ENDCASE => attr1 _ attr2 _ FALSE; RPush[type, attr]; phraseNP _ saveNP}; TypeTest: PROC [node: Tree.Index, from, to: CSEIndex] = { subType: CSEIndex = CanonicalType[from]; op: NarrowOp = Narrowing[type: subType, target: to]; SELECT TRUE FROM op.error => Log.ErrorTree[typeClash, tb[node].son[1]]; op.computed => Log.ErrorTree[missingBinding, tb[node].son[1]]; op.unImpl => Log.Warning[unimplemented]; ENDCASE; IF subType # from THEN tb[node].son[1] _ ForceType[tb[node].son[1], subType]; tb[node].attr1 _ op.indirect; IF (tb[node].attr2 _ op.rtTest) THEN EnterType[MarkedType[to]]; tb[node].attr3 _ op.tagTest}; EndPoint: PROC [node: Tree.Index] = { OPEN tb[node]; type: CSEIndex; son[1] _ TypeExp[son[1]]; type _ UnderType[TypeForTree[son[1]]]; BEGIN WITH seb[type] SELECT FROM basic => SELECT code FROM codeINT, codeCHAR => NULL; ENDCASE => GO TO fail; enumerated => NULL; relative => IF TypeForm[offsetType] # subrange THEN GO TO fail; subrange => NULL; long => IF NormalType[UnderType[rangeType]] # dataPtr.typeINT THEN GO TO fail; ENDCASE => GO TO fail; EXITS fail => Log.ErrorTree[typeClash, son[1]]; END; RPush[type, fullAttr]}; Unspec: PROC [type: CSEIndex] RETURNS [BOOL] = { RETURN [WITH t: seb[type] SELECT FROM basic => t.code = codeANY, ENDCASE => FALSE]}; SafeForUnspec: PROC [target: CSEIndex] RETURNS [BOOL] = { RETURN [P3S.safety # checked OR RCType[target] = none]}; Rhs: PUBLIC PROC [exp: Tree.Link, lhsType: CSEIndex] RETURNS [val: Tree.Link] = { rhsType: CSEIndex; val _ Exp[exp, lhsType]; rhsType _ rStack[rI].type; SELECT TRUE FROM (lhsType = rhsType), Unspec[lhsType] => NULL; ENDCASE => { -- immediate matching is inconclusive UNTIL Types.Assignable[[own, lhsType], [own, rhsType]] DO WITH t: seb[rhsType] SELECT FROM subrange => rhsType _ UnderType[t.rangeType]; record => { IF Bundling[rhsType] = 0 THEN GO TO nomatch; rhsType _ Unbundle[LOOPHOLE[rhsType, RecordSEIndex]]; val _ ForceType[val, IF Unspec[rhsType] THEN typeANY ELSE rhsType]}; ref, arraydesc => { SELECT seb[lhsType].typeTag FROM long => { IF ~Types.Assignable[[own, NormalType[lhsType]], [own, rhsType]] THEN GO TO nomatch; val _ Lengthen[val, lhsType]}; ENDCASE => GO TO nomatch; rhsType _ lhsType}; basic => { IF Unspec[rhsType] AND SafeForUnspec[lhsType] THEN SELECT seb[lhsType].typeTag FROM long => val _ Lengthen[val, MakeLongType[typeANY, lhsType]]; ENDCASE ELSE SELECT seb[lhsType].typeTag FROM long => { IF ~Types.Assignable[[own, NormalType[lhsType]], [own, rhsType]] THEN GO TO nomatch; val _ Lengthen[val, lhsType]}; real => IF rhsType = dataPtr.typeINT THEN { val _ Float[val, rhsType, lhsType]; rStack[rI].attr.const _ FALSE} ELSE GO TO nomatch; ENDCASE => GO TO nomatch; rhsType _ lhsType}; long => { subType: CSEIndex = NormalType[rhsType]; SELECT seb[lhsType].typeTag FROM long => SELECT TRUE FROM Unspec[NormalType[lhsType]] => lhsType _ rhsType; Unspec[subType] AND SafeForUnspec[lhsType] => rhsType _ lhsType; ENDCASE => GO TO nomatch; real => IF subType = dataPtr.typeINT THEN { val _ Float[val, rhsType, lhsType]; rStack[rI].attr.const _ FALSE; rhsType _ lhsType} ELSE GO TO nomatch; basic, subrange => { IF ~Types.Assignable[[own, subType], [own, lhsType]] THEN GO TO nomatch; rhsType _ UnderType[t.rangeType]; val _ Shorten[val, rhsType]}; enumerated => IF EqualTypes[rhsType, dataPtr.typeATOM] THEN { Log.ErrorTree[missingCoercion, val]; rhsType _ lhsType} ELSE GOTO nomatch; ENDCASE => GO TO nomatch}; ENDCASE => GO TO nomatch; REPEAT nomatch => { -- no coercion is possible Log.ErrorTree[typeClash, IF exp = Tree.Null THEN implicit.tree ELSE val]; rhsType _ lhsType}; ENDLOOP; rStack[rI].type _ rhsType}; IF seb[rhsType].typeTag = transfer AND OperandInline[val] THEN Log.ErrorTree[misusedInline, val]; RETURN}; GenericRhs: PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = { type: CSEIndex; val _ Exp[exp, target]; type _ rStack[rI].type; DO WITH seb[type] SELECT FROM subrange => type _ UnderType[rangeType]; record => { IF Bundling[type] = 0 THEN EXIT; type _ Unbundle[LOOPHOLE[type, RecordSEIndex]]; val _ ForceType[val, type]}; ENDCASE => EXIT; rStack[rI].type _ type; ENDLOOP; SELECT seb[target].typeTag FROM enumerated => IF EqualTypes[type, dataPtr.typeATOM] THEN { Log.ErrorTree[missingCoercion, val]; rStack[rI].type _ target}; ENDCASE; RETURN}; BalancedRhs: PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = { type: CSEIndex; val _ Exp[exp, target]; SELECT seb[target].typeTag FROM long, real => { type _ CanonicalType[rStack[rI].type]; IF type # typeANY AND seb[target].typeTag # seb[type].typeTag AND EqualTypes[NormalType[target], type] THEN { SELECT seb[target].typeTag FROM long => IF seb[type].typeTag # real THEN val _ Lengthen[val, target]; real => {val _ Float[val, type, target]; rStack[rI].attr.const _ FALSE}; ENDCASE; rStack[rI].type _ target}}; enumerated => IF EqualTypes[rStack[rI].type, dataPtr.typeATOM] THEN { Log.ErrorTree[missingCoercion, val]; rStack[rI].type _ target}; ENDCASE; RETURN}; AttrClass: PROC [type: CSEIndex] RETURNS [{short, long, real}] = { RETURN [WITH t: seb[type] SELECT FROM long => long, real => real, relative => AttrClass[UnderType[t.offsetType]], ENDCASE => short]}; SetAttributes: PROC [node: Tree.Index] = { SELECT AttrClass[rStack[rI].type] FROM long => {tb[node].attr1 _ FALSE; tb[node].attr2 _ TRUE}; real => {tb[node].attr1 _ TRUE; tb[node].attr2 _ FALSE}; ENDCASE => tb[node].attr1 _ tb[node].attr2 _ FALSE}; BalanceAttributes: PROC [node: Tree.Index] = { lType, rType: CSEIndex; lType _ rStack[rI-1].type; rType _ rStack[rI].type; SELECT AttrClass[lType] FROM long => { SELECT AttrClass[rType] FROM long => {tb[node].attr1 _ FALSE; tb[node].attr2 _ TRUE}; real => { rStack[rI-1].type _ rType; tb[node].son[1] _ Float[tb[node].son[1], lType, rType]; rStack[rI-1].attr.const _ FALSE; tb[node].attr1 _ TRUE; tb[node].attr2 _ FALSE}; ENDCASE => { rStack[rI].type _ rType _ MakeLongType[rType, lType]; tb[node].son[2] _ Lengthen[tb[node].son[2], rType]; tb[node].attr1 _ FALSE; tb[node].attr2 _ TRUE}}; real => { tb[node].attr1 _ TRUE; tb[node].attr2 _ FALSE; SELECT AttrClass[rType] FROM real => NULL; ENDCASE => { rStack[rI].type _ lType; tb[node].son[2] _ Float[tb[node].son[2], rType, lType]; rStack[rI].attr.const _ FALSE}}; ENDCASE => SELECT AttrClass[rType] FROM long => { rStack[rI-1].type _ lType _ MakeLongType[lType, rType]; tb[node].son[1] _ Lengthen[tb[node].son[1], lType]; tb[node].attr1 _ FALSE; tb[node].attr2 _ TRUE}; real => { rStack[rI-1].type _ rType; tb[node].son[1] _ Float[tb[node].son[1], lType, rType]; rStack[rI-1].attr.const _ FALSE; tb[node].attr1 _ TRUE; tb[node].attr2 _ FALSE}; ENDCASE => tb[node].attr1 _ tb[node].attr2 _ FALSE}; Lengthen: PROC [t: Tree.Link, target: CSEIndex] RETURNS [Tree.Link] = { PushTree[t]; PushNode[lengthen, 1]; SetInfo[target]; RETURN [PopTree[]]}; Shorten: PROC [t: Tree.Link, target: CSEIndex] RETURNS [Tree.Link] = { PushTree[t]; PushNode[shorten, 1]; SetInfo[target]; RETURN [PopTree[]]}; Float: PROC [t: Tree.Link, type, target: CSEIndex] RETURNS [Tree.Link] = { PushTree[IF seb[type].typeTag = long THEN t ELSE Lengthen[t, MakeLongType[type, typeANY]]]; SELECT NormalType[type] FROM dataPtr.typeINT => {PushNode[float, 1]; SetInfo[target]}; typeANY => NULL; ENDCASE => Log.ErrorTree[typeClash, t]; RETURN [PopTree[]]}; implicit: PUBLIC P3S.ImplicitInfo; -- implied attributes of Tree.Null Exp: PUBLIC PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = { type: CSEIndex; phraseNP _ none; IF exp = Tree.Null THEN {RPush[implicit.type, implicit.attr]; RETURN [Tree.Null]}; WITH e: exp SELECT FROM symbol => { sei: ISEIndex = e.index; attr: Attr; attr.noXfer _ attr.noAssign _ TRUE; RecordMention[sei]; type _ UnderType[seb[sei].idType]; SELECT ctxb[seb[sei].idCtx].ctxType FROM included => IF ~(attr.const_ConstantId[sei]) THEN Log.ErrorSei[unimplemented, sei]; imported => attr.const _ ConstantId[sei]; ENDCASE => attr.const _ seb[sei].constant; RPush[type, attr]; val _ exp}; hash => WITH t: seb[target] SELECT FROM enumerated => { sei: ISEIndex; IF ([sei: sei] _ SearchCtxList[e.index, t.valueCtx]).found THEN { RPush[target, fullAttr]; val _ [symbol[sei]]} ELSE val _ Id[e.index]}; ENDCASE => val _ Id[e.index]; literal => { attr: Attr; attr.noXfer _ attr.noAssign _ TRUE; WITH e.index SELECT FROM string => { [val, type] _ StringRef[exp, target]; attr.const _ FALSE; IF dataPtr.interface THEN Log.ErrorTree[unimplemented, exp]}; ENDCASE => {type _ dataPtr.typeINT; attr.const _ TRUE; val _ exp}; RPush[type, attr]}; subtree => { node: Tree.Index _ e.index; val _ exp; -- the default SELECT tb[node].name FROM dot => {node _ Dot[node, target]; val _ [subtree[node]]}; uparrow => UpArrow[node]; apply => { node _ Apply[node, target, FALSE]; val _ [subtree[node]]; CheckNonVoid[node, target]}; uminus, abs => UnaryOp[node]; plus => Plus[node]; minus => Minus[node]; times, div, mod => ArithOp[node]; relE, relN => RelOp[node, FALSE]; relL, relGE, relG, relLE => RelOp[node, TRUE]; in, notin => In[node]; not => tb[node].son[1] _ Rhs[tb[node].son[1], dataPtr.typeBOOL]; or, and => BoolOp[node]; ifx => IfExp[node, target]; casex => SelectExp[node, target, Case, TRUE]; bindx => SelectExp[node, target, Discrimination, FALSE]; assignx => Assignment[node]; extractx => {Extract[node]; CheckNonVoid[node, target]}; min, max => MinMax[node, target]; pred, succ => EnumOp[node, target]; addr, base, length, arraydesc => AddrOp[node, target]; all => All[node, target]; mwconst => IF tb[node].attr1 THEN RPush[dataPtr.typeREAL, fullAttr] ELSE { IF longUnsigned = CSENull THEN longUnsigned _ MakeLongType[dataPtr.idCARDINAL, typeANY]; RPush[longUnsigned, fullAttr]}; void => RPush[target, voidAttr]; clit => RPush[dataPtr.typeCHAR, fullAttr]; llit => { attr: Attr _ fullAttr; attr.const _ FALSE; RPush[dataPtr.typeSTRING, attr]}; atom => { hti: HTIndex = GetHash[tb[node].son[1]]; WITH t: seb[target] SELECT FROM enumerated => { sei: ISEIndex; IF ~([sei: sei]_SearchCtxList[hti, t.valueCtx]).found THEN Log.ErrorHti[unknownId, hti]; tb[node].son[1] _ Tree.Null; FreeNode[node]; node _ Tree.NullIndex; val _ [symbol[index: sei]]; RPush[target, fullAttr]}; ENDCASE => { SymLiteralOps.EnterAtom[hti]; EnterType[dataPtr.typeAtomRecord, FALSE]; RPush[dataPtr.typeATOM, fullAttr]}}; nil => { OPEN tb[node]; SELECT TRUE FROM (son[1] # Tree.Null) => { son[1] _ TypeExp[son[1]]; type _ UnderType[TypeForTree[son[1]]]}; (target # typeANY) => type _ target; ENDCASE => type _ MakeRefType[typeANY, typeANY]; IF ~NullableType[type] THEN Log.ErrorTree[typeClash, val]; RPush[type, fullAttr]}; new, signalx, errorx, fork, joinx, create, startx, cons, listcons => { val _ MiscXfer[node, target]; node _ GetNode[val]; CheckNonVoid[node, target]}; syserrorx => { RPush[CSENull, emptyAttr]; CheckNonVoid[node, target]}; lengthen => { OPEN tb[node]; subType: CSEIndex; son[1] _ GenericRhs[ son[1], WITH seb[target] SELECT FROM long => TargetType[UnderType[rangeType]], ENDCASE => target]; subType _ TargetType[rStack[rI].type]; IF subType = dataPtr.typeINT OR seb[subType].typeTag = ref OR seb[subType].typeTag = arraydesc OR subType = typeANY THEN rStack[rI].type _ MakeLongType[subType, target] ELSE {Log.ErrorTree[typeClash, son[1]]; rStack[rI].type _ typeANY}}; narrow => { OPEN tb[node]; IF son[2] = Tree.Null THEN { IF target = typeANY THEN Log.ErrorNode[noTarget, node]; type _ target} ELSE {son[2] _ TypeExp[son[2]]; type _ UnderType[TypeForTree[son[2]]]}; son[1] _ Exp[son[1], TargetType[type]]; TypeTest[node: node, from: rStack[rI].type, to: type]; IF attr3 AND ~attr1 AND son[2] = Tree.Null THEN Log.ErrorNode[noTarget, node]; IF RCType[type] = simple THEN { nType: CSEIndex = NormalType[type]; WITH t: seb[nType] SELECT FROM ref => EnterType[t.refType, FALSE]; ENDCASE => NULL}; IF tb[node].nSons > 2 THEN [] _ CatchPhrase[tb[node].son[3]]; rStack[rI].type _ type; rStack[rI].attr.const _ rStack[rI].attr.noXfer _ FALSE}; istype => { OPEN tb[node]; son[1] _ Exp[son[1], typeANY]; son[2] _ TypeExp[son[2]]; type _ UnderType[TypeForTree[son[2]]]; TypeTest[node: node, from: RType[], to: type]; rStack[rI].type _ dataPtr.typeBOOL; rStack[rI].attr.const _ FALSE}; safen => tb[node].son[1] _ Exp[tb[node].son[1], target]; loophole => { OPEN tb[node]; subType: CSEIndex; son[1] _ Exp[son[1], typeANY]; subType _ RType[]; IF seb[subType].typeTag = transfer AND OperandInline[son[1]] THEN Log.ErrorTree[misusedInline, son[1]]; IF son[2] = Tree.Null THEN { IF target = typeANY THEN Log.ErrorNode[noTarget, node]; rStack[rI].type _ target} ELSE { son[2] _ TypeExp[son[2]]; rStack[rI].type _ UnderType[TypeForTree[son[2]]]}; IF RCType[rStack[rI].type] # none THEN { rStack[rI].attr.const _ FALSE; IF P3S.safety = checked THEN Log.ErrorNode[unsafeOperation, node]}}; size => { OPEN tb[node]; attr: Attr; son[1] _ TypeAppl[son[1]]; attr _ RAttr[]; RPop[]; IF son[2] # Tree.Null THEN { saveNP: NPUse = phraseNP; son[2] _ Rhs[son[2], dataPtr.typeINT]; attr _ And[attr, RAttr[]]; RPop[]; phraseNP _ MergeNP[saveNP][phraseNP]}; RPush[dataPtr.typeINT, attr]}; first, last => EndPoint[node]; typecode => { tb[node].son[1] _ TypeExp[tb[node].son[1]]; EnterType[TypeForTree[tb[node].son[1]], FALSE]; RPush[typeANY, fullAttr]}; self => { val _ P3S.self.tree; P3S.self.tree _ Tree.Null; phraseNP _ P3S.self.np; RPush[P3S.self.type, P3S.self.attr]; FreeNode[node]; node _ Tree.NullIndex}; val, cast => { tb[node].son[1] _ Exp[tb[node].son[1], target]; rStack[rI].type _ target}; ord => IF UniOperand[node] THEN { tb[node].son[1] _ Exp[tb[node].son[1], typeANY]; SetAttributes[node]; IF ~IndexType[RType[]] THEN Log.ErrorTree[typeClash, tb[node].son[1]]; rStack[rI].type _ MakeNumeric[RType[]]}; stringinit => { tb[node].son[2] _ Rhs[tb[node].son[2], dataPtr.typeINT]; IF ~RAttr[].const THEN Log.ErrorTree[nonConstant, tb[node].son[2]]; RPop[]; RPush[dataPtr.typeSTRING, voidAttr]}; ENDCASE => IF tb[node].name = item THEN tb[node].son[2] _ Exp[tb[node].son[2], target] ELSE {Log.Error[unimplemented]; RPush[typeANY, emptyAttr]}; IF node # Tree.NullIndex THEN tb[node].info _ rStack[rI].type}; ENDCASE; RETURN}; CheckNonVoid: PROC [node: Tree.Index, target: CSEIndex] = { IF rStack[rI].type = CSENull THEN SELECT tb[node].name FROM error => {tb[node].name _ errorx; rStack[rI].type _ target}; errorx, syserrorx => rStack[rI].type _ target; ENDCASE => {Log.ErrorNode[typeClash, node]; rStack[rI].type _ typeANY}}; VoidExp: PUBLIC PROC [exp: Tree.Link] RETURNS [val: Tree.Link] = { val _ Exp[exp, typeANY]; RPop[]; RETURN}; UniOperand: PROC [node: Tree.Index] RETURNS [valid: BOOL] = { l: CARDINAL = ListLength[tb[node].son[1]]; IF ~(valid _ l=1) THEN { IF l > 1 THEN Log.ErrorN[listLong, l-1] ELSE Log.ErrorN[listShort, l+1]; tb[node].son[1] _ UpdateList[tb[node].son[1], VoidExp]; RPush[typeANY, emptyAttr]}; RETURN}; StringRef: PROC [t: Tree.Link, target: CSEIndex] RETURNS [v: Tree.Link, type: CSEIndex] = { IF RCType[target] = none THEN {type _ dataPtr.typeSTRING; v _ t} ELSE { nType: CSEIndex = NormalType[target]; rType: SEIndex = WITH t: seb[nType] SELECT FROM ref => t.refType, ENDCASE => dataPtr.idTEXT; form: TextForm = TextRep[rType]; cType: SEIndex = IF form = text THEN dataPtr.idTEXT ELSE rType; type _ textType[form]; IF type = CSENull THEN { type _ MakeLongType[MakeRefType[cType: cType, hint: nType, counted: TRUE], target]; textType[form] _ type}; EnterType[TypeRoot[cType], FALSE]; WITH e: t SELECT FROM literal => WITH e.index SELECT FROM string => { sti _ LiteralOps.FindHeapString[sti, TypeRoot[cType]]; SymLiteralOps.EnterText[sti]}; ENDCASE; ENDCASE; PushTree[t]; PushNode[textlit, 1]; SetAttr[2, TRUE]; SetInfo[type]; v _ PopTree[]}; RETURN}; }. file Pass3Xb.mesa last modified by Satterthwaite, March 9, 1983 3:13 pm last modified by Donahue, 9-Dec-81 15:32:12 called by allocator whenever table area is repacked intermediate result bookkeeping type manipulation check bundling operators arithmetic expression manipulation put value in canonical form expressions val => IF UniOperand[node] THEN { IF ~IndexType[target] THEN Log.ErrorNode[noTarget, node]; tb[node].son[1] _ EvalNumeric[tb[node].son[1]]; rStack[rI].type _ target}; overloaded string literals ÊŒ˜Jšœ™Jšœ5™5Jšœ,™,J˜šÏk ˜ šœœœ˜J˜MJ˜N—Jšœœœ ˜šœ œœ˜J˜*J˜M—Jšœ œœ˜(JšœœœD˜Sšœœœ˜J˜$J˜BJ˜FJ˜GJ˜GJ˜,—Jšœœœ.˜=Jšœœœ˜1šœ œœ˜J˜CJ˜D—Jšœ œœA˜VJšœœœ5˜Ešœ œœ˜J˜DJ˜—Jšœœœ+˜š˜J˜šœœ˜˜J˜"J˜!šœœ œ˜IJ˜%—Jšœ˜—˜ Jšœœœœ˜'Jšœœ˜1—Jšœœœ˜—š˜˜ Jšœœ"˜8J˜——Jšœ˜ J˜J˜———Jšœ"™"˜šŸ œœœ˜9šœœ˜%J˜,Jšœ˜J˜——šŸ œœœ˜=J˜%šœ˜'Jšœœ˜J˜:Jšœ"˜)—Jšœ˜J˜—šŸœœ˜$Jšœ ˜J˜J˜1J˜J˜J˜Jšœœ"˜>J˜Jšœœ˜Jšœœ˜Jšœ5œ˜;Jšœœ˜ Jšœ%˜,——šœ˜šœ˜Jšœœ˜!JšœB˜I—J˜—šœœ˜9J˜%—J˜J˜J˜—Jšœ-œ˜CJ˜=Jšœœ œ˜8J˜'J˜J˜—šŸœœ)˜5Jšœ ˜J˜ J˜Jšœ œ˜J˜J˜˜J˜J˜J˜?J˜"˜%Jšœœ+˜G—šœœœ˜.Jšœœ#˜@J˜Jšœ œ(˜8—Jšœœœ˜"J˜—Jšœ,œ˜CJ˜'šœ˜Jšœœ œ˜&Jšœœ œœ˜:Jšœœ˜!—J˜'J˜J˜—šŸœœ+˜9J˜(J˜4šœœ˜J˜6J˜>J˜(Jšœ˜—Jšœœ7˜MJ˜Jšœœ˜?J˜J˜J˜—šŸœœ˜%Jšœ ˜J˜J˜˜&Jš˜šœ œ˜˜šœ˜Jšœœ˜Jšœœœ˜——Jšœœ˜Jš œ œ!œœœ˜?Jšœ œ˜˜Jšœ4œœœ˜F—Jšœœœ˜—š˜J˜)—Jšœ˜—J˜J˜J˜—šŸœœœœ˜0šœœœ˜%J˜Jšœœ˜J˜——šŸ œœœœ˜9Jšœœ˜8J˜J˜—šŸœœœ%œ˜QJ˜J˜J˜šœœ˜Jšœ(œ˜-šœž%˜3šœ2˜9šœœ˜ J˜-˜ Jšœœœœ ˜,Jšœœ˜5Jšœœœ œ ˜D—˜šœ˜ ˜ šœ?˜EJšœœ ˜—J˜—Jšœœœ ˜—J˜—˜ šœœ˜2šœ˜ J˜J˜"—Jšœ˜J˜J˜—šŸ œœ$œ˜PJ˜J˜0Jšœ™š˜šœ œ˜J˜(˜ Jšœœœ˜ Jšœœ˜/J˜—Jšœœ˜—J˜Jšœ˜—šœ˜˜ šœ$œ˜,J˜@——Jšœ˜—Jšœ˜J˜—šŸ œœ$œ˜QJ˜J˜šœ˜˜J˜&šœœ(˜=šœ&œ˜/šœ˜Jšœœœ˜EJšœAœ˜HJšœ˜—J˜———˜ šœ/œ˜7J˜@——Jšœ˜—Jšœ˜J˜J˜—šŸ œœœ˜Bšœœœ˜%J˜ J˜ J˜/Jšœ ˜J˜——šŸ œœ˜*šœ˜&Jšœœœ˜8Jšœœœ˜8Jšœ&œ˜4J˜——šŸœœ˜.J˜J˜4šœ˜˜ šœ˜Jšœœœ˜8˜ J˜J˜7Jšœœ˜ Jšœœœ˜0—šœ˜ J˜5J˜3Jšœœœ˜1———˜ Jšœœœ˜/šœ˜Jšœœ˜ šœ˜ J˜J˜7Jšœœ˜ ———šœ˜ šœ˜˜ J˜7J˜3Jšœœœ˜0—˜ J˜J˜7Jšœœ˜ Jšœœœ˜0—Jšœ&œ˜4J˜————šŸœœ"œ˜GJšœ5œ˜IJ˜—šŸœœ"œ˜FJšœ4œ˜HJ˜—šŸœœ(œ˜Jšœ œ˜$Jšœ˜Jšœ+˜/—šœ˜J˜9Jšœ œ˜Jšœ ˜'—Jšœ˜J˜J˜——Jšœ ™ ˜Jšœ œž"˜EJ˜šŸœœœ$œ˜PJ˜J˜Jšœœ'œ˜Ršœœ˜˜ J˜J˜ Jšœœ˜8J˜"šœ˜(˜ Jšœœ"˜G—J˜)Jšœ#˜*—J˜—˜šœœ˜˜J˜šœ9œ˜AJ˜.—Jšœ˜—Jšœ˜——˜ J˜ Jšœœ˜#šœ œ˜˜ J˜%Jšœ œ˜Jšœœ$˜=—Jšœ*œ ˜B—J˜—˜ J˜Jšœ ž˜Jšœ˜˜J˜9J˜J˜˜ Jšœœ˜9J˜J˜—J˜J˜J˜J˜!Jšœœ˜!Jšœ(œ˜.J˜J˜@J˜J˜Jšœ'œ˜-Jšœ1œ˜8J˜J˜8J˜!J˜#J˜6J˜J˜˜ Jšœœ"˜8šœ˜šœ˜J˜9—J˜J˜——J˜ J˜*J˜˜ J˜Jšœ œ$˜6J˜—˜ J˜(šœœ˜˜J˜šœ4˜:J˜—J˜EJ˜6—šœ˜ Jšœ@œ˜GJ˜$J˜———˜Jšœ ˜šœœ˜˜J˜B—J˜$Jšœ)˜0—Jšœœ˜:J˜J˜—˜FJ˜J˜2J˜—˜J˜7J˜—˜ Jšœ ˜J˜˜J˜šœ œ˜J˜)Jšœ ˜——J˜&šœ˜Jšœ˜Jšœ!˜#Jšœœ0˜I—Jšœ@˜DJ˜—˜ Jšœ ˜šœœ˜Jšœœ˜7J˜—JšœC˜GJ˜'J˜6Jšœœœœ˜Nšœœ˜J˜#šœœ˜Jšœœ˜#Jšœœ˜——Jšœœ#˜=J˜Jšœ1œ˜8J˜—˜ Jšœ ˜J˜J˜@J˜.Jšœ<œ˜CJ˜—J˜8J˜˜ Jšœ ˜J˜J˜2šœ!œ˜AJ˜%—šœœ˜Jšœœ˜7J˜—šœ˜J˜J˜2—šœ œ˜(Jšœœ˜Jšœœ(˜DJ˜——˜ Jšœ ˜J˜ J˜2šœœ˜J˜J˜&J˜"J˜&—J˜J˜—J˜J˜˜ J˜+Jšœ(œ˜/J˜J˜—˜ J˜0J˜=J˜(J˜—J˜˜ J˜/J˜J˜—˜šœœ˜J˜FJšœœ+˜FJ˜(J˜————————Jšœ™Jšœ™Jšœ9™9Jšœ/™/Jšœ™˜˜J˜8Jšœœ-˜CJ˜.J˜—šœ˜ Jšœœ/˜KJšœ7˜;J˜—Jšœœ"˜?Jšœ˜Jšœ˜J˜šŸ œœ)˜;šœ˜!šœ˜J˜