MODULE ContextualDependency;
IMPORT
Files, KernelLog, Configuration, Texts, Strings,
XML, XMLObjects, XMLParser, XMLScanner;
CONST
RangeDebug = FALSE;
LanguageAttribute = "language";
ValueAttribute = "value";
BaseAttribute = "base";
CodeAttribute = "code";
RangeAttribute = "range";
SizeAttribute = "size";
OffsetAttribute = "offset";
NegativeAttribute = "neg";
LowAttribute = "low";
HighAttribute = "high";
RootTag = "LanguageContext";
CharacterTag = "Character";
PropertiesTag = "Properties";
ContextTag = "Context";
SecondleftTag = "secondleft";
LeftTag = "left";
RightTag = "right";
SecondrightTag = "secondright";
GenericLeftTag = "genericLeft";
GenericRightTag = "genericRight";
BeforeTag = "left";
AfterTag = "right";
ResultTag = "result";
RangeTag = "Range";
SizeTag = "Size";
TYPE
String = POINTER TO ARRAY OF Texts.Char32;
RangeNode = POINTER TO RECORD
leftNode, rightNode : RangeNode;
leftOuterBound, leftInnerBound : LONGINT;
rightInnerBound, rightOuterBound : LONGINT;
depth : LONGINT;
target : RangedContextAnalyzer;
END;
RangeTree = OBJECT
VAR
root, last : RangeNode;
PROCEDURE AddRange(target : RangedContextAnalyzer);
VAR
newNode : RangeNode;
dummyLeft, dummyRight : LONGINT;
BEGIN
NEW(newNode);
newNode.leftInnerBound := target.rangeLow;
newNode.leftOuterBound := target.rangeLow;
newNode.rightInnerBound := target.rangeHigh;
newNode.rightOuterBound := target.rangeHigh;
newNode.target := target;
newNode.depth := 1;
dummyLeft := -1;
dummyRight := -1;
RecursiveAdd(newNode,root,dummyLeft,dummyRight);
END AddRange;
PROCEDURE RecursiveAdd(newNode : RangeNode; VAR thisNode : RangeNode; VAR left, right : LONGINT);
VAR
newIntermediateNode : RangeNode;
BEGIN
IF thisNode = NIL THEN
thisNode := newNode;
ELSE
IF thisNode.target = NIL THEN
IF newNode.rightOuterBound < thisNode.rightInnerBound THEN
RecursiveAdd(newNode,thisNode.leftNode,left,right);
thisNode.leftOuterBound := left;
thisNode.leftInnerBound := right;
right := thisNode.rightOuterBound;
ELSIF newNode.leftOuterBound > thisNode.leftInnerBound THEN
RecursiveAdd(newNode,thisNode.rightNode,left,right);
thisNode.rightOuterBound := right;
thisNode.rightInnerBound := left;
left := thisNode.leftOuterBound;
END;
ELSE
NEW(newIntermediateNode);
IF newNode.rightOuterBound < thisNode.leftOuterBound THEN
newIntermediateNode.leftNode := newNode;
newIntermediateNode.rightNode := thisNode;
newIntermediateNode.leftOuterBound := newNode.leftOuterBound;
newIntermediateNode.leftInnerBound := newNode.rightOuterBound;
newIntermediateNode.rightOuterBound := thisNode.rightOuterBound;
newIntermediateNode.rightInnerBound := thisNode.leftOuterBound;
newIntermediateNode.depth := thisNode.depth + 1;
thisNode := newIntermediateNode;
left := newIntermediateNode.leftOuterBound;
right := newIntermediateNode.rightOuterBound;
ELSIF newNode.leftOuterBound > thisNode.rightOuterBound THEN
newIntermediateNode.leftNode := thisNode;
newIntermediateNode.rightNode := newNode;
newIntermediateNode.leftOuterBound := thisNode.leftOuterBound;
newIntermediateNode.leftInnerBound := thisNode.rightOuterBound;
newIntermediateNode.rightOuterBound := newNode.rightOuterBound;
newIntermediateNode.rightInnerBound := newNode.leftOuterBound;
newIntermediateNode.depth := thisNode.depth + 1;
thisNode := newIntermediateNode;
left := newIntermediateNode.leftOuterBound;
right := newIntermediateNode.rightOuterBound;
ELSE
left := thisNode.leftOuterBound;
right := thisNode.rightOuterBound;
END;
END;
END;
END RecursiveAdd;
PROCEDURE Search(position : LONGINT) : RangedContextAnalyzer;
BEGIN
IF last # NIL THEN
IF (position >= last.leftOuterBound) & (position <= last.rightOuterBound) THEN
RETURN last.target;
END;
END;
RETURN RecursiveSearch(position,root);
END Search;
PROCEDURE RecursiveSearch(position : LONGINT; thisNode : RangeNode) : RangedContextAnalyzer;
BEGIN
IF thisNode = NIL THEN
RETURN NIL
ELSIF thisNode.target = NIL THEN
IF (position >= thisNode.leftOuterBound) & (position <= thisNode.leftInnerBound) THEN
RETURN RecursiveSearch(position,thisNode.leftNode);
ELSIF (position >= thisNode.rightInnerBound) & (position <= thisNode.rightOuterBound) THEN
RETURN RecursiveSearch(position,thisNode.rightNode);
ELSE
RETURN NIL;
END;
ELSE
IF (position >= thisNode.leftOuterBound) & (position <= thisNode.rightOuterBound) THEN
last := thisNode;
RETURN thisNode.target;
ELSE
RETURN NIL;
END;
END;
END RecursiveSearch;
PROCEDURE CompleteBalancing;
VAR
dummyInt : LONGINT;
BEGIN
dummyInt := RecursiveBalancing(root);
END CompleteBalancing;
PROCEDURE RecursiveBalancing(VAR thisNode : RangeNode) : LONGINT;
VAR
leftDepth, rightDepth : LONGINT;
BEGIN
IF thisNode = NIL THEN
RETURN 0;
ELSIF thisNode.target = NIL THEN
leftDepth := RecursiveBalancing(thisNode.leftNode);
rightDepth := RecursiveBalancing(thisNode.rightNode);
IF leftDepth > rightDepth + 1 THEN
IF GetNodeDepth(thisNode.leftNode.leftNode) < GetNodeDepth(thisNode.leftNode.rightNode) THEN
RotateLeft(thisNode.leftNode);
END;
RotateRight(thisNode);
RETURN thisNode.depth;
ELSIF leftDepth + 1 < rightDepth THEN
IF GetNodeDepth(thisNode.rightNode.leftNode) > GetNodeDepth(thisNode.rightNode.rightNode) THEN
RotateRight(thisNode.rightNode);
END;
RotateLeft(thisNode);
RETURN thisNode.depth;
ELSE
IF Strings.Max(leftDepth,rightDepth) = leftDepth THEN
RETURN leftDepth + 1;
ELSE
RETURN rightDepth + 1;
END;
END;
ELSE
RETURN 1;
END;
END RecursiveBalancing;
PROCEDURE RotateRight(VAR thisNode : RangeNode);
VAR
tempNode : RangeNode;
BEGIN
tempNode := thisNode.leftNode.rightNode;
thisNode.leftNode.rightNode := thisNode;
thisNode := thisNode.leftNode;
thisNode.rightNode.leftNode := tempNode;
thisNode.rightNode.depth := Strings.Max(GetNodeDepth(thisNode.rightNode.leftNode),GetNodeDepth(thisNode.rightNode.rightNode)) + 1;
thisNode.depth := Strings.Max(thisNode.leftNode.depth,thisNode.rightNode.depth) + 1;
END RotateRight;
PROCEDURE RotateLeft(VAR thisNode : RangeNode);
VAR
tempNode : RangeNode;
BEGIN
tempNode := thisNode.rightNode.leftNode;
thisNode.rightNode.leftNode := thisNode;
thisNode := thisNode.rightNode;
thisNode.leftNode.rightNode := tempNode;
thisNode.leftNode.depth := Strings.Max(GetNodeDepth(thisNode.leftNode.leftNode),GetNodeDepth(thisNode.leftNode.rightNode)) + 1;
thisNode.depth := Strings.Max(thisNode.leftNode.depth,thisNode.rightNode.depth) + 1;
END RotateLeft;
PROCEDURE GetNodeDepth(thisNode : RangeNode) : LONGINT;
BEGIN
IF thisNode = NIL THEN
RETURN 0;
ELSE
RETURN thisNode.depth;
END;
END GetNodeDepth;
END RangeTree;
Range = OBJECT
VAR
lowerBound : LONGINT;
upperBound : LONGINT;
valid : BOOLEAN;
PROCEDURE &Init*;
BEGIN
lowerBound := -1;
upperBound := -1;
valid := TRUE;
END Init;
END Range;
RangeArray = POINTER TO ARRAY OF Range;
Ranges = POINTER TO RECORD
posRanges : RangeArray;
negRanges : RangeArray;
END;
GenericContext = POINTER TO RECORD
lastRanges, secondlastRanges : Ranges;
nextRanges, secondnextRanges : Ranges;
wholeLastRanges, wholeNextRanges : POINTER TO ARRAY OF Ranges;
resultingChar : Texts.Char32;
END;
ContextCacheElement = OBJECT
VAR
key : Texts.Char32;
value : GenericContext;
next : ContextCacheElement;
PROCEDURE &Init*(key : Texts.Char32; value : GenericContext);
BEGIN
SELF.key := key;
SELF.value := value;
next := NIL;
END Init;
END ContextCacheElement;
ContextCache = OBJECT
VAR
internalCache: POINTER TO ARRAY OF ContextCacheElement;
cacheSize : LONGINT;
nextElement : ContextCacheElement;
PROCEDURE &Init*(hashMapSize : LONGINT);
BEGIN
cacheSize := hashMapSize;
NEW(internalCache,cacheSize);
END Init;
PROCEDURE Lookup(char : Texts.Char32; VAR done : BOOLEAN) : GenericContext;
VAR
bucket : LONGINT;
currentElement : ContextCacheElement;
BEGIN
IF ~done & (nextElement # NIL) THEN
currentElement := nextElement;
ELSE
bucket := char MOD cacheSize;
currentElement := internalCache[bucket];
END;
WHILE currentElement # NIL DO
IF currentElement.key = char THEN
IF currentElement.next = NIL THEN
done := TRUE;
nextElement := NIL;
ELSE
done := FALSE;
nextElement := currentElement.next;
END;
RETURN currentElement.value;
ELSE
currentElement := currentElement.next;
END;
END;
done := TRUE;
nextElement := NIL;
RETURN NIL;
END Lookup;
PROCEDURE Insert(char : Texts.Char32; value : GenericContext);
VAR
newElement : ContextCacheElement;
bucket : LONGINT;
BEGIN
NEW(newElement,char,value);
bucket := char MOD cacheSize;
newElement.next := internalCache[bucket];
internalCache[bucket] := newElement;
END Insert;
END ContextCache;
RangedContextAnalyzer = OBJECT
VAR
language : XML.String;
rangeLow, rangeHigh : LONGINT;
closeContext, wideContext, wholeContext : BOOLEAN;
contextCache : ContextCache;
contextFile : XML.Document;
PROCEDURE &Init*(context : XML.Document; rangeLow, rangeHigh : LONGINT; language, mode : Strings.String);
BEGIN
NEW(contextCache,rangeHigh-rangeLow);
contextFile := context;
SELF.rangeLow := rangeLow;
SELF.rangeHigh := rangeHigh;
SELF.language := language;
IF mode^ = "close" THEN
SetCloseContext;
ELSIF mode^ = "wide" THEN
SetWideContext;
ELSIF mode^ = "whole" THEN
SetWholeContext;
ELSE
SetNoContext;
END;
END Init;
PROCEDURE SetCloseContext;
BEGIN
closeContext := TRUE;
wideContext := FALSE;
wholeContext := FALSE;
END SetCloseContext;
PROCEDURE SetWideContext;
BEGIN
closeContext := FALSE;
wideContext := TRUE;
wholeContext := FALSE;
END SetWideContext;
PROCEDURE SetWholeContext;
BEGIN
closeContext := FALSE;
wideContext := FALSE;
wholeContext := TRUE;
END SetWholeContext;
PROCEDURE SetNoContext;
BEGIN
closeContext := FALSE;
wideContext := FALSE;
wholeContext := FALSE;
END SetNoContext;
PROCEDURE AnalyzeCloseContext(thisChar, lastChar, nextChar : Texts.Char32) : Texts.Char32;
VAR
done,contextFound,validContextFound : BOOLEAN;
thisContext : GenericContext;
BEGIN
done := TRUE;
contextFound := FALSE;
validContextFound := FALSE;
REPEAT
IF RangeDebug THEN
KernelLog.String("looking for "); KernelLog.Hex(thisChar,4); KernelLog.Ln;
END;
thisContext := contextCache.Lookup(thisChar,done);
IF (thisContext # NIL) THEN
IF RangeDebug THEN
KernelLog.String("found a context..."); KernelLog.Ln;
END;
IF CheckCloseContext(lastChar,nextChar,thisContext) THEN
IF RangeDebug THEN
KernelLog.String("... which is valid."); KernelLog.Ln;
END;
done := TRUE;
validContextFound := TRUE;
ELSE
IF RangeDebug THEN
KernelLog.String("... which is invalid."); KernelLog.Ln;
END;
END;
contextFound := TRUE;
END;
UNTIL done;
IF ~contextFound THEN
IF RangeDebug THEN
KernelLog.String("No contexts found. Getting contexts from XML file"); KernelLog.Ln;
END;
GetContextsFromXML(thisChar);
RETURN AnalyzeCloseContext(thisChar,lastChar,nextChar);
ELSE
IF (thisContext = NIL) OR ~validContextFound THEN
IF RangeDebug THEN
KernelLog.String("No change done.."); KernelLog.Ln;
END;
RETURN thisChar;
ELSE
IF RangeDebug THEN
KernelLog.String("replacing "); KernelLog.Hex(thisChar,4);
KernelLog.String(" with "); KernelLog.Hex(thisContext.resultingChar,4);
KernelLog.Ln;
END;
RETURN thisContext.resultingChar;
END;
END;
END AnalyzeCloseContext;
PROCEDURE AnalyzeWideContext(thisChar, secondlastChar, lastChar, nextChar, secondnextChar : Texts.Char32) : Texts.Char32;
VAR
done,contextFound,validContextFound : BOOLEAN;
thisContext : GenericContext;
BEGIN
done := TRUE;
contextFound := FALSE;
validContextFound := FALSE;
REPEAT
thisContext := contextCache.Lookup(thisChar,done);
IF (thisContext # NIL) THEN
IF CheckWideContext(secondlastChar,lastChar,nextChar,secondnextChar,thisContext) THEN
done := TRUE;
validContextFound := TRUE;
END;
contextFound := TRUE;
END;
UNTIL done;
IF ~contextFound THEN
GetContextsFromXML(thisChar);
RETURN AnalyzeWideContext(thisChar,secondlastChar,lastChar,nextChar,secondnextChar);
ELSE
IF (thisContext = NIL) OR ~validContextFound THEN
RETURN thisChar;
ELSE
RETURN thisContext.resultingChar;
END;
END;
END AnalyzeWideContext;
PROCEDURE AnalyzeWholeContext(thisPos : LONGINT; line : String) : Texts.Char32;
VAR
thisChar : Texts.Char32;
done,contextFound,validContextFound : BOOLEAN;
thisContext : GenericContext;
BEGIN
IF (thisPos < 0) OR (thisPos > LEN(line) - 1) THEN
RETURN 0H;
ELSE
thisChar := line[thisPos];
END;
done := TRUE;
contextFound := FALSE;
validContextFound := FALSE;
REPEAT
thisContext := contextCache.Lookup(thisChar,done);
IF (thisContext # NIL) THEN
IF CheckWholeContext(thisPos,line,thisContext) THEN
done := TRUE;
validContextFound := TRUE;
END;
contextFound := TRUE;
END;
UNTIL done;
IF ~contextFound THEN
GetContextsFromXML(thisChar);
RETURN AnalyzeWholeContext(thisPos,line);
ELSE
IF (thisContext = NIL) OR ~validContextFound THEN
RETURN thisChar;
ELSE
RETURN thisContext.resultingChar;
END;
END;
END AnalyzeWholeContext;
PROCEDURE CheckCloseContext(lastChar, nextChar : Texts.Char32; context : GenericContext) : BOOLEAN;
BEGIN
IF context = NIL THEN RETURN TRUE END;
RETURN RangeOK(lastChar,context.lastRanges) & RangeOK(nextChar,context.nextRanges);
END CheckCloseContext;
PROCEDURE CheckWideContext(secondlastChar, lastChar, nextChar, secondnextChar : Texts.Char32; context : GenericContext) : BOOLEAN;
BEGIN
IF context = NIL THEN RETURN TRUE END;
RETURN RangeOK(secondlastChar,context.secondlastRanges) & RangeOK(lastChar,context.lastRanges) &
RangeOK(nextChar,context.nextRanges) & RangeOK(secondnextChar,context.secondnextRanges);
END CheckWideContext;
PROCEDURE CheckWholeContext(thisPos : LONGINT; line : String; context : GenericContext) : BOOLEAN;
VAR
i,j : LONGINT;
BEGIN
IF context = NIL THEN RETURN TRUE END;
IF (context.wholeLastRanges = NIL) & (context.wholeNextRanges = NIL) THEN
RETURN FALSE;
END;
IF (context.wholeLastRanges # NIL) & (thisPos > 0) THEN
j := 0;
i := thisPos - 1;
WHILE (i >= 0) & (j < LEN(context.wholeLastRanges)) DO
IF ~RangeOK(line[i],context.wholeLastRanges[j]) THEN
RETURN FALSE;
END;
DEC(i);
INC(j);
END;
END;
IF (context.wholeNextRanges # NIL) &(thisPos < LEN(line) - 1) THEN
j := 0;
i := thisPos + 1;
WHILE (i < LEN(line)) & (j < LEN(context.wholeNextRanges)) DO
IF ~RangeOK(line[i],context.wholeNextRanges[j]) THEN
RETURN FALSE;
END;
INC(i);
INC(j);
END;
END;
RETURN TRUE;
END CheckWholeContext;
PROCEDURE RangeOK(thisChar : Texts.Char32; ranges : Ranges) : BOOLEAN;
VAR
i : LONGINT;
rangeOK : BOOLEAN;
BEGIN
IF ranges = NIL THEN RETURN TRUE END;
rangeOK := FALSE;
IF ranges.posRanges # NIL THEN
i := 0;
LOOP
IF i > LEN(ranges.posRanges) - 1 THEN
IF i = 0 THEN
rangeOK := TRUE;
END;
EXIT;
END;
IF (ranges.posRanges[i].lowerBound <= thisChar) & (thisChar <= ranges.posRanges[i].upperBound) THEN
rangeOK := TRUE;
EXIT;
END;
INC(i);
END;
END;
IF ~rangeOK THEN
RETURN FALSE;
ELSIF ranges.negRanges = NIL THEN
RETURN TRUE;
END;
i := 0;
LOOP
IF i > LEN(ranges.negRanges) - 1 THEN
RETURN TRUE;
END;
IF (ranges.negRanges[i].lowerBound <= thisChar) & (thisChar <= ranges.negRanges[i].upperBound) THEN
RETURN FALSE;
END;
INC(i);
END;
RETURN rangeOK;
END RangeOK;
PROCEDURE GetContextsFromXML(thisChar : Texts.Char32);
VAR
newContext : GenericContext;
beforeRanges, afterRanges : Ranges;
root : XML.Element;
tagName, languageAttribute, baseAttribute, rangeAttribute, offsetAttribute, negAttribute, resultAttribute, sizeAttribute : XML.String;
charElements, contextElements, rangeElements, genericBeforeElements, genericAfterElements : XMLObjects.Enumerator;
charElement, contextElement, rangeElement, genericBeforeElement, genericAfterElement : ANY;
contextFound, validContext, charFound : BOOLEAN;
charString : XML.String;
charCode : Texts.Char32;
res : LONGINT;
base16, neg : BOOLEAN;
genericRangeSize, offset : LONGINT;
BEGIN
res := 0;
contextFound := FALSE;
charFound := FALSE;
validContext := FALSE;
IF contextFile # NIL THEN
root := contextFile.GetRoot();
tagName := root.GetName();
languageAttribute := root.GetAttributeValue(LanguageAttribute);
IF (tagName^ = RootTag) & (languageAttribute^ = language^) THEN
baseAttribute := root.GetAttributeValue(BaseAttribute);
base16 := baseAttribute^ = "Hex";
charElements := root.GetContents();
WHILE ~charFound & charElements.HasMoreElements() DO
charElement := charElements.GetNext();
WITH charElement : XML.Element DO
tagName := charElement.GetName();
IF tagName^ = CharacterTag THEN
charString := charElement.GetAttributeValue(CodeAttribute);
IF base16 THEN
Strings.HexStrToInt(charString^,charCode,res);
ELSE
Strings.StrToInt(charString^,charCode);
END;
IF (res >= 0) & (charCode = thisChar) THEN
charFound := TRUE;
IF RangeDebug THEN
KernelLog.String("range for "); KernelLog.Hex(charCode,4); KernelLog.Ln;
END;
contextElements := charElement.GetContents();
WHILE contextElements.HasMoreElements() DO
contextElement := contextElements.GetNext();
WITH contextElement : XML.Element DO
tagName := contextElement.GetName();
IF tagName^ = ContextTag THEN
NEW(newContext);
contextFound := FALSE;
validContext := FALSE;
rangeElements := contextElement.GetContents();
IF RangeDebug THEN
KernelLog.String("Insert ranges: "); KernelLog.Ln;
END;
WHILE rangeElements.HasMoreElements() DO
rangeElement := rangeElements.GetNext();
WITH rangeElement : XML.Element DO
tagName := rangeElement.GetName();
IF (tagName^ = SecondleftTag) & wideContext THEN
rangeAttribute := rangeElement.GetAttributeValue(RangeAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
ParseRangeString(rangeAttribute,base16,neg,newContext.secondlastRanges);
validContext := TRUE;
ELSIF (tagName^ = LeftTag) & (wideContext OR closeContext) THEN
IF RangeDebug THEN KernelLog.String("last: ") END;
rangeAttribute := rangeElement.GetAttributeValue(RangeAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
ParseRangeString(rangeAttribute,base16,neg,newContext.lastRanges);
validContext := TRUE;
ELSIF (tagName^ = RightTag) & (closeContext OR wideContext) THEN
IF RangeDebug THEN KernelLog.String("next: ") END;
rangeAttribute := rangeElement.GetAttributeValue(RangeAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
ParseRangeString(rangeAttribute,base16,neg,newContext.nextRanges);
validContext := TRUE;
ELSIF (tagName^ = SecondrightTag) & wideContext THEN
rangeAttribute := rangeElement.GetAttributeValue(RangeAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
ParseRangeString(rangeAttribute,base16,neg,newContext.secondnextRanges);
validContext := TRUE;
ELSIF (tagName^ = GenericLeftTag) & wholeContext THEN
sizeAttribute := rangeElement.GetAttributeValue(SizeAttribute);
Strings.StrToInt(sizeAttribute^,genericRangeSize);
NEW(newContext.wholeLastRanges,genericRangeSize);
genericBeforeElements := rangeElement.GetContents();
WHILE genericBeforeElements.HasMoreElements() DO
genericBeforeElement := genericBeforeElements.GetNext();
WITH genericBeforeElement : XML.Element DO
tagName := genericBeforeElement.GetName();
IF tagName^ = BeforeTag THEN
rangeAttribute := genericBeforeElement.GetAttributeValue(RangeAttribute);
offsetAttribute := genericBeforeElement.GetAttributeValue(OffsetAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
Strings.StrToInt(offsetAttribute^,offset);
IF offset < 1 THEN offset := 1; END;
NEW(beforeRanges);
ParseRangeString(rangeAttribute,base16,neg,beforeRanges);
newContext.wholeLastRanges[offset-1] := beforeRanges;
END;
END;
END;
validContext := TRUE;
ELSIF (tagName^ = GenericRightTag) & wholeContext THEN
sizeAttribute := rangeElement.GetAttributeValue(SizeAttribute);
Strings.StrToInt(sizeAttribute^,genericRangeSize);
NEW(newContext.wholeNextRanges,genericRangeSize);
genericAfterElements := rangeElement.GetContents();
WHILE genericAfterElements.HasMoreElements() DO
genericAfterElement := genericAfterElements.GetNext();
WITH genericAfterElement : XML.Element DO
tagName := genericAfterElement.GetName();
IF tagName^ = AfterTag THEN
rangeAttribute := genericAfterElement.GetAttributeValue(RangeAttribute);
offsetAttribute := genericAfterElement.GetAttributeValue(OffsetAttribute);
negAttribute := rangeElement.GetAttributeValue(NegativeAttribute);
IF (negAttribute = NIL) OR (negAttribute^ # "!") THEN
neg := FALSE;
ELSE
neg := TRUE;
END;
Strings.StrToInt(offsetAttribute^,offset);
IF offset < 1 THEN offset := 1; END;
NEW(afterRanges);
ParseRangeString(rangeAttribute,base16,neg,afterRanges);
newContext.wholeNextRanges[offset-1] := afterRanges;
END;
END;
END;
validContext := TRUE;
ELSIF tagName^ = ResultTag THEN
resultAttribute := rangeElement.GetAttributeValue(CodeAttribute);
IF base16 THEN
Strings.HexStrToInt(resultAttribute^,newContext.resultingChar,res);
ELSE
Strings.StrToInt(resultAttribute^,newContext.resultingChar);
END;
IF newContext.resultingChar = 0 THEN
newContext.resultingChar := 200BH;
END;
IF RangeDebug THEN
KernelLog.String("resulting in: ");
KernelLog.Hex(newContext.resultingChar,4);
KernelLog.Ln;
END;
END;
END;
END;
IF validContext THEN
contextCache.Insert(thisChar,newContext);
contextFound := TRUE;
END;
END;
END
END;
END;
END;
END;
END;
IF ~contextFound THEN
IF RangeDebug THEN
KernelLog.String("inserting reflexive context"); KernelLog.Ln;
END;
contextCache.Insert(thisChar,GetReflexiveContext(thisChar));
END;
END;
END;
END GetContextsFromXML;
PROCEDURE ParseRangeString(rangeString : XML.String; base16, neg : BOOLEAN; VAR ranges : Ranges);
VAR
numberOfRanges, i, j, k, res : LONGINT;
tempString : XML.String;
range : Range;
theseRanges, oldRanges : RangeArray;
BEGIN
NEW(tempString,LEN(rangeString));
IF LEN(rangeString) > 0 THEN
IF rangeString[0] = "X" THEN
IF RangeDebug THEN
KernelLog.String("any character"); KernelLog.Ln;
END;
RETURN;
END;
numberOfRanges := 1;
ELSE
numberOfRanges := 0;
END;
FOR i := 0 TO LEN(rangeString) - 1 DO
IF rangeString[i] = ";" THEN
INC(numberOfRanges);
END;
END;
IF ranges = NIL THEN
NEW(ranges);
END;
IF neg THEN
oldRanges := ranges.negRanges;
ELSE
oldRanges := ranges.posRanges;
END;
IF oldRanges # NIL THEN
NEW(theseRanges,numberOfRanges+LEN(oldRanges));
FOR k := 0 TO LEN(oldRanges) - 1 DO
theseRanges[k] := oldRanges[k];
END;
ELSE
NEW(theseRanges,numberOfRanges);
k := 0;
END;
NEW(range);
j := 0;
FOR i := 0 TO LEN(rangeString) - 1 DO
tempString[j] := rangeString[i];
IF rangeString[i] = "!" THEN
IF i = 0 THEN
range.valid := FALSE;
ELSIF rangeString[i-1] = ";" THEN
range.valid := FALSE;
END;
ELSIF rangeString[i] = "-" THEN
tempString[j] := 0X;
IF base16 THEN
Strings.HexStrToInt(tempString^,range.lowerBound,res);
ELSE
Strings.StrToInt(tempString^,range.lowerBound);
END;
j := 0;
ELSIF rangeString[i] = ";" THEN
tempString[j] := 0X;
IF tempString^ = "O" THEN
range.lowerBound := -1;
range.upperBound := -1;
ELSE
IF base16 THEN
Strings.HexStrToInt(tempString^,range.upperBound,res);
ELSE
Strings.StrToInt(tempString^,range.upperBound);
END;
IF range.lowerBound < 0 THEN
range.lowerBound := range.upperBound;
END;
END;
IF RangeDebug THEN
KernelLog.String("["); KernelLog.Hex(range.lowerBound,4);
KernelLog.String(","); KernelLog.Hex(range.upperBound,4);
KernelLog.String("]");
IF ~range.valid THEN KernelLog.String(" (!)") END;
KernelLog.Ln;
END;
theseRanges[k] := range;
NEW(range);
INC(k);
j := 0;
ELSE
INC(j);
END;
END;
IF tempString^ = "O" THEN
range.lowerBound := -1;
range.upperBound := -1;
ELSE
IF base16 THEN
Strings.HexStrToInt(tempString^,range.upperBound,res);
ELSE
Strings.StrToInt(tempString^,range.upperBound);
END;
IF range.lowerBound < 0 THEN
range.lowerBound := range.upperBound;
END;
END;
IF RangeDebug THEN
KernelLog.String("["); KernelLog.Hex(range.lowerBound,4);
KernelLog.String(","); KernelLog.Hex(range.upperBound,4);
KernelLog.String("]");
IF ~range.valid THEN KernelLog.String(" (!)") END;
KernelLog.Ln;
END;
theseRanges[k] := range;
IF neg THEN
ranges.negRanges := theseRanges;
ELSE
ranges.posRanges := theseRanges;
END;
END ParseRangeString;
PROCEDURE GetReflexiveContext(thisChar : Texts.Char32) : GenericContext;
VAR
newContext : GenericContext;
BEGIN
NEW(newContext);
newContext.resultingChar := thisChar;
RETURN newContext;
END GetReflexiveContext;
END RangedContextAnalyzer;
ContextAnalyzer = OBJECT
VAR
PROCEDURE &Init*;
BEGIN
NEW(ranges);
END Init;
PROCEDURE AnalyzeLine(line : Texts.TextReader; start, end : LONGINT) : Texts.TextReader;
VAR
lineCache, newLine : String;
ch,ch1,ch2,ch3,ch4 : Texts.Char32;
i : LONGINT;
analyzer : RangedContextAnalyzer;
newText : Texts.Text;
oneCharString : Texts.PUCS32String;
newTextReader : Texts.TextReader;
BEGIN
line.text.AcquireRead;
IF start < 0 THEN
start := 0;
END;
IF end < 0 THEN
end := line.text.GetLength() - 1;
END;
NEW(lineCache,end-start+1);
NEW(newLine,end-start+1);
line.SetPosition(start);
line.SetDirection(1);
i := 0;
ch := 0;
FOR i:= 0 TO LEN(lineCache) - 1 DO
line.ReadCh(ch);
lineCache[i] := ch;
END;
line.text.ReleaseRead;
NEW(newText);
NEW(oneCharString,2);
oneCharString[1] := 0H;
newText.AcquireWrite;
FOR i := 0 TO LEN(lineCache) - 1 DO
IF i = 0 THEN
ch1 := -1;
ch2 := -1;
ELSIF i = 1 THEN
ch1 := -1;
ch2 := lineCache[0];
ELSE
ch1 := lineCache[i-2];
ch2 := lineCache[i-1];
END;
ch := lineCache[i];
IF i = LEN(lineCache) - 1 THEN
ch3 := -1;
ch4 := -1;
ELSIF i = LEN(lineCache) - 2 THEN
ch3 := lineCache[i+1];
ch4 := -1;
ELSE
ch3 := lineCache[i+1];
ch4 := lineCache[i+2];
END;
analyzer := ranges.Search(lineCache[i]);
IF analyzer # NIL THEN
IF RangeDebug THEN
KernelLog.String("==> "); KernelLog.Hex(ch,4); KernelLog.Ln;
END;
IF analyzer.closeContext THEN
ch := analyzer.AnalyzeCloseContext(ch,ch2,ch3);
ELSIF analyzer.wideContext THEN
ch := analyzer.AnalyzeWideContext(ch,ch1,ch2,ch3,ch4);
ELSIF analyzer.wholeContext THEN
ch := analyzer.AnalyzeWholeContext(i,lineCache);
END;
IF RangeDebug THEN
KernelLog.String("<== "); KernelLog.Hex(ch,4); KernelLog.Ln;
END;
END;
oneCharString[0] := ch;
newText.InsertUCS32(i,oneCharString^);
END;
newText.ReleaseWrite;
NEW(newTextReader,newText);
RETURN newTextReader;
END AnalyzeLine;
END ContextAnalyzer;
VAR
contextAnalyzer : ContextAnalyzer;
ranges : RangeTree;
PROCEDURE RegisterRangedAnalyzer(language : Strings.String; contextFile : XML.Document);
VAR
newAnalyzer : RangedContextAnalyzer;
root : XML.Element;
charElements, propertyElements : XMLObjects.Enumerator;
charElement, propertyElement : ANY;
tagName, languageAttribute, baseAttribute : XML.String;
base16, propertyFound : BOOLEAN;
rangeLow, rangeHigh, res : LONGINT;
mode, tempRangeLow, tempRangeHigh : Strings.String;
BEGIN
propertyFound := FALSE;
IF (contextFile # NIL) & (ranges # NIL) THEN
root := contextFile.GetRoot();
tagName := root.GetName();
languageAttribute := root.GetAttributeValue(LanguageAttribute);
IF (tagName^ = RootTag) & (languageAttribute^ = language^) THEN
baseAttribute := root.GetAttributeValue(BaseAttribute);
base16 := baseAttribute^ = "Hex";
charElements := root.GetContents();
WHILE ~propertyFound & charElements.HasMoreElements() DO
charElement := charElements.GetNext();
WITH charElement : XML.Element DO
tagName := charElement.GetName();
IF tagName^ = PropertiesTag THEN
propertyElements := charElement.GetContents();
WHILE propertyElements.HasMoreElements() DO
propertyElement := propertyElements.GetNext();
WITH propertyElement : XML.Element DO
tagName := propertyElement.GetName();
IF tagName^ = RangeTag THEN
tempRangeLow := propertyElement.GetAttributeValue(LowAttribute);
tempRangeHigh := propertyElement.GetAttributeValue(HighAttribute);
IF base16 THEN
Strings.HexStrToInt(tempRangeLow^,rangeLow,res);
Strings.HexStrToInt(tempRangeHigh^,rangeHigh,res);
ELSE
Strings.StrToInt(tempRangeLow^,rangeLow);
Strings.StrToInt(tempRangeHigh^,rangeHigh);
END;
ELSIF tagName^ = SizeTag THEN
mode := propertyElement.GetAttributeValue(ValueAttribute);
END;
END;
END;
NEW(newAnalyzer,contextFile,rangeLow,rangeHigh,language,mode);
ranges.AddRange(newAnalyzer);
propertyFound := TRUE;
END;
END;
END;
END;
END;
END RegisterRangedAnalyzer;
PROCEDURE AnalyzeLine*(line : Texts.TextReader; start, end : LONGINT) : Texts.TextReader;
BEGIN
RETURN contextAnalyzer.AnalyzeLine(line,start,end);
END AnalyzeLine;
PROCEDURE InitRangedAnalyzer(CONST filename : ARRAY OF CHAR; VAR useThisContext : BOOLEAN; VAR context : XML.Document);
TYPE
Trap = OBJECT
VAR
xmlError : BOOLEAN;
filename: Files.FileName;
PROCEDURE &InitTrap (CONST filename: ARRAY OF CHAR);
BEGIN COPY (filename, SELF.filename); xmlError := FALSE;
END InitTrap;
PROCEDURE Handler(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
BEGIN
KernelLog.String("Error in ");
KernelLog.String(filename);
KernelLog.String(" at position ");
KernelLog.String("pos= "); KernelLog.Int(pos, 0); KernelLog.String(" line= "); KernelLog.Int(line, 0); KernelLog.String(" row= "); KernelLog.Int(row, 0); KernelLog.Ln;
xmlError := TRUE;
END Handler;
END Trap;
VAR
file: Files.File;
scanner: XMLScanner.Scanner;
parser: XMLParser.Parser;
reader: Files.Reader;
trap: Trap;
BEGIN
context := NIL;
file := Files.Old(filename);
IF file # NIL THEN
NEW(reader, file, 0);
NEW(scanner, reader);
NEW(parser, scanner);
NEW(trap, filename);
parser.reportError := trap.Handler;
context:= parser.Parse();
IF ~trap.xmlError THEN
context := NIL;
useThisContext := TRUE;
ELSE
useThisContext := FALSE;
END;
ELSE
KernelLog.String("Error opening ");
KernelLog.String(filename);
KernelLog.String(". File not found.");
KernelLog.Ln;
useThisContext := FALSE;
END;
END InitRangedAnalyzer;
PROCEDURE LoadContextualDependencies;
VAR
contextSection : XML.Element;
rangePropertyElements : XMLObjects.Enumerator;
rangePropertyElement : ANY;
useNewContext : BOOLEAN;
newContextFile : XML.Document;
filenameAttribute, languageAttribute : Strings.String;
BEGIN
contextSection := Configuration.GetSection("Context");
IF (contextSection # NIL) THEN
rangePropertyElements:= contextSection.GetContents();
WHILE rangePropertyElements.HasMoreElements() DO
rangePropertyElement := rangePropertyElements.GetNext();
WITH rangePropertyElement : XML.Element DO
filenameAttribute := rangePropertyElement.GetAttributeValue("value");
languageAttribute := rangePropertyElement.GetAttributeValue("name");
InitRangedAnalyzer(filenameAttribute^,useNewContext,newContextFile);
IF useNewContext THEN
IF RangeDebug THEN
KernelLog.String(filenameAttribute^); KernelLog.String(" loaded."); KernelLog.Ln;
END;
RegisterRangedAnalyzer(languageAttribute,newContextFile);
END;
END;
END;
ranges.CompleteBalancing;
ELSE
KernelLog.String("ContextDependecy: Could not load contextual dependecies (missing 'Context' section in configuration file).");
KernelLog.Ln;
END;
END LoadContextualDependencies;
BEGIN
NEW(contextAnalyzer);
LoadContextualDependencies;
END ContextualDependency.