530 lines
17 KiB
C
530 lines
17 KiB
C
/*
|
|
* Copyright (C) 2014-2019 Muhammad Tayyab Akram
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <SBConfig.h>
|
|
|
|
#include "BidiChain.h"
|
|
#include "BracketQueue.h"
|
|
#include "BracketType.h"
|
|
#include "LevelRun.h"
|
|
#include "PairingLookup.h"
|
|
#include "SBAssert.h"
|
|
#include "SBBase.h"
|
|
#include "SBLog.h"
|
|
#include "IsolatingRun.h"
|
|
|
|
static void ResolveAvailableBracketPairs(IsolatingRunRef isolatingRun);
|
|
|
|
static void AttachLevelRunLinks(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
LevelRunRef baseLevelRun = isolatingRun->baseLevelRun;
|
|
LevelRunRef current;
|
|
LevelRunRef next;
|
|
|
|
isolatingRun->_originalLink = BidiChainGetNext(chain, chain->roller);
|
|
BidiChainSetNext(chain, chain->roller, baseLevelRun->firstLink);
|
|
|
|
/* Iterate over level runs and attach their links to form an isolating run. */
|
|
for (current = baseLevelRun; (next = current->next); current = next) {
|
|
BidiChainSetNext(chain, current->lastLink, next->firstLink);
|
|
}
|
|
BidiChainSetNext(chain, current->lastLink, chain->roller);
|
|
|
|
isolatingRun->_lastLevelRun = current;
|
|
isolatingRun->_sos = RunExtrema_SOR(baseLevelRun->extrema);
|
|
|
|
if (!RunKindIsPartialIsolate(baseLevelRun->kind)) {
|
|
isolatingRun->_eos = RunExtrema_EOR(current->extrema);
|
|
} else {
|
|
SBLevel paragraphLevel = isolatingRun->paragraphLevel;
|
|
SBLevel runLevel = baseLevelRun->level;
|
|
SBLevel eosLevel = (runLevel > paragraphLevel ? runLevel : paragraphLevel);
|
|
isolatingRun->_eos = ((eosLevel & 1) ? SBBidiTypeR : SBBidiTypeL);
|
|
}
|
|
}
|
|
|
|
static void AttachOriginalLinks(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
LevelRunRef current;
|
|
|
|
BidiChainSetNext(chain, chain->roller, isolatingRun->_originalLink);
|
|
|
|
/* Iterate over level runs and attach original subsequent links. */
|
|
for (current = isolatingRun->baseLevelRun; current; current = current->next) {
|
|
BidiChainSetNext(chain, current->lastLink, current->subsequentLink);
|
|
}
|
|
}
|
|
|
|
static BidiLink ResolveWeakTypes(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
BidiLink roller = chain->roller;
|
|
BidiLink link;
|
|
|
|
BidiLink priorLink;
|
|
SBBidiType sos;
|
|
|
|
SBBidiType w1PriorType;
|
|
SBBidiType w2StrongType;
|
|
SBBidiType w4PriorType;
|
|
SBBidiType w5PriorType;
|
|
SBBidiType w7StrongType;
|
|
|
|
priorLink = roller;
|
|
sos = isolatingRun->_sos;
|
|
|
|
w1PriorType = sos;
|
|
w2StrongType = sos;
|
|
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
SBBoolean forceMerge = SBFalse;
|
|
|
|
/* Rule W1 */
|
|
if (type == SBBidiTypeNSM) {
|
|
/* Change the 'type' variable as well because it can be EN on which W2 depends. */
|
|
type = (SBBidiTypeIsIsolate(w1PriorType) ? SBBidiTypeON : w1PriorType);
|
|
BidiChainSetType(chain, link, type);
|
|
|
|
/* Fix for 3rd point of rule N0. */
|
|
if (w1PriorType == SBBidiTypeON) {
|
|
forceMerge = SBTrue;
|
|
}
|
|
}
|
|
w1PriorType = type;
|
|
|
|
/* Rule W2 */
|
|
if (type == SBBidiTypeEN) {
|
|
if (w2StrongType == SBBidiTypeAL) {
|
|
BidiChainSetType(chain, link, SBBidiTypeAN);
|
|
}
|
|
}
|
|
/*
|
|
* Rule W3
|
|
* NOTE: It is safe to apply W3 in 'else-if' statement because it only depends on type AL.
|
|
* Even if W2 changes EN to AN, there won't be any harm.
|
|
*/
|
|
else if (type == SBBidiTypeAL) {
|
|
BidiChainSetType(chain, link, SBBidiTypeR);
|
|
}
|
|
|
|
if (SBBidiTypeIsStrong(type)) {
|
|
/* Save the strong type as it is checked in W2. */
|
|
w2StrongType = type;
|
|
}
|
|
|
|
if ((type != SBBidiTypeON && BidiChainGetType(chain, priorLink) == type) || forceMerge) {
|
|
BidiChainAbandonNext(chain, priorLink);
|
|
} else {
|
|
priorLink = link;
|
|
}
|
|
}
|
|
|
|
priorLink = roller;
|
|
w4PriorType = sos;
|
|
w5PriorType = sos;
|
|
w7StrongType = sos;
|
|
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
SBBidiType nextType = BidiChainGetType(chain, BidiChainGetNext(chain, link));
|
|
|
|
/* Rule W4 */
|
|
if (BidiChainIsSingle(chain, link)
|
|
&& SBBidiTypeIsNumberSeparator(type)
|
|
&& SBBidiTypeIsNumber(w4PriorType)
|
|
&& (w4PriorType == nextType)
|
|
&& (w4PriorType == SBBidiTypeEN || type == SBBidiTypeCS))
|
|
{
|
|
/* Change the current type as well because it can be EN on which W5 depends. */
|
|
type = w4PriorType;
|
|
BidiChainSetType(chain, link, type);
|
|
}
|
|
w4PriorType = type;
|
|
|
|
/* Rule W5 */
|
|
if (type == SBBidiTypeET && (w5PriorType == SBBidiTypeEN || nextType == SBBidiTypeEN)) {
|
|
/* Change the current type as well because it is EN on which W7 depends. */
|
|
type = SBBidiTypeEN;
|
|
BidiChainSetType(chain, link, type);
|
|
}
|
|
w5PriorType = type;
|
|
|
|
switch (type) {
|
|
/* Rule W6 */
|
|
case SBBidiTypeET:
|
|
case SBBidiTypeCS:
|
|
case SBBidiTypeES:
|
|
BidiChainSetType(chain, link, SBBidiTypeON);
|
|
break;
|
|
|
|
/*
|
|
* Rule W7
|
|
* NOTE: W7 is expected to be applied after W6. However this is not the case here. The
|
|
* reason is that W6 can only create the type ON which is not tested in W7 by any
|
|
* means. So it won't affect the algorithm.
|
|
*/
|
|
case SBBidiTypeEN:
|
|
if (w7StrongType == SBBidiTypeL) {
|
|
BidiChainSetType(chain, link, SBBidiTypeL);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Save the strong type for W7.
|
|
* NOTE: The strong type is expected to be saved after applying W7 because W7 itself creates
|
|
* a strong type. However the strong type being saved here is based on the type after
|
|
* W5. This won't effect the algorithm because a single link contains all consecutive
|
|
* EN types. This means that even if W7 creates a strong type, it will be saved in
|
|
* next iteration.
|
|
*/
|
|
case SBBidiTypeL:
|
|
case SBBidiTypeR:
|
|
w7StrongType = type;
|
|
break;
|
|
}
|
|
|
|
if (type != SBBidiTypeON && BidiChainGetType(chain, priorLink) == type) {
|
|
BidiChainAbandonNext(chain, priorLink);
|
|
} else {
|
|
priorLink = link;
|
|
}
|
|
}
|
|
|
|
return priorLink;
|
|
}
|
|
|
|
static void ResolveBrackets(IsolatingRunRef isolatingRun)
|
|
{
|
|
const SBCodepointSequence *sequence = isolatingRun->codepointSequence;
|
|
SBUInteger paragraphOffset = isolatingRun->paragraphOffset;
|
|
BracketQueueRef queue = &isolatingRun->_bracketQueue;
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
BidiLink roller = chain->roller;
|
|
BidiLink link;
|
|
|
|
BidiLink priorStrongLink;
|
|
SBLevel runLevel;
|
|
|
|
priorStrongLink = BidiLinkNone;
|
|
runLevel = isolatingRun->baseLevelRun->level;
|
|
|
|
BracketQueueReset(queue, SBLevelAsNormalBidiType(runLevel));
|
|
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBUInteger stringIndex;
|
|
SBCodepoint codepoint;
|
|
SBBidiType type;
|
|
|
|
SBCodepoint bracketValue;
|
|
BracketType bracketType;
|
|
|
|
type = BidiChainGetType(chain, link);
|
|
|
|
switch (type) {
|
|
case SBBidiTypeON:
|
|
stringIndex = BidiChainGetOffset(chain, link) + paragraphOffset;
|
|
codepoint = SBCodepointSequenceGetCodepointAt(sequence, &stringIndex);
|
|
bracketValue = LookupBracketPair(codepoint, &bracketType);
|
|
|
|
switch (bracketType) {
|
|
case BracketTypeOpen:
|
|
if (queue->count < BracketQueueGetMaxCapacity()) {
|
|
BracketQueueEnqueue(queue, priorStrongLink, link, bracketValue);
|
|
} else {
|
|
goto Resolve;
|
|
}
|
|
break;
|
|
|
|
case BracketTypeClose:
|
|
if (queue->count != 0) {
|
|
BracketQueueClosePair(queue, link, codepoint);
|
|
|
|
if (BracketQueueShouldDequeue(queue)) {
|
|
ResolveAvailableBracketPairs(isolatingRun);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SBBidiTypeEN:
|
|
case SBBidiTypeAN:
|
|
type = SBBidiTypeR;
|
|
|
|
case SBBidiTypeR:
|
|
case SBBidiTypeL:
|
|
if (queue->count != 0) {
|
|
BracketQueueSetStrongType(queue, type);
|
|
}
|
|
|
|
priorStrongLink = link;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Resolve:
|
|
ResolveAvailableBracketPairs(isolatingRun);
|
|
}
|
|
|
|
static void ResolveAvailableBracketPairs(IsolatingRunRef isolatingRun)
|
|
{
|
|
BracketQueueRef queue = &isolatingRun->_bracketQueue;
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
|
|
SBLevel runLevel;
|
|
SBBidiType embeddingDirection;
|
|
SBBidiType oppositeDirection;
|
|
|
|
runLevel = isolatingRun->baseLevelRun->level;
|
|
embeddingDirection = SBLevelAsNormalBidiType(runLevel);
|
|
oppositeDirection = SBLevelAsOppositeBidiType(runLevel);
|
|
|
|
while (queue->count != 0) {
|
|
BidiLink openingLink = BracketQueueGetOpeningLink(queue);
|
|
BidiLink closingLink = BracketQueueGetClosingLink(queue);
|
|
|
|
if ((openingLink != BidiLinkNone) && (closingLink != BidiLinkNone)) {
|
|
SBBidiType innerStrongType;
|
|
SBBidiType pairType;
|
|
|
|
innerStrongType = BracketQueueGetStrongType(queue);
|
|
|
|
/* Rule: N0.b */
|
|
if (innerStrongType == embeddingDirection) {
|
|
pairType = innerStrongType;
|
|
}
|
|
/* Rule: N0.c */
|
|
else if (innerStrongType == oppositeDirection) {
|
|
BidiLink priorStrongLink;
|
|
SBBidiType priorStrongType;
|
|
|
|
priorStrongLink = BracketQueueGetPriorStrongLink(queue);
|
|
|
|
if (priorStrongLink != BidiLinkNone) {
|
|
BidiLink link;
|
|
|
|
priorStrongType = BidiChainGetType(chain, priorStrongLink);
|
|
if (SBBidiTypeIsNumber(priorStrongType)) {
|
|
priorStrongType = SBBidiTypeR;
|
|
}
|
|
|
|
link = BidiChainGetNext(chain, priorStrongLink);
|
|
|
|
while (link != openingLink) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
if (type == SBBidiTypeL || type == SBBidiTypeR) {
|
|
priorStrongType = type;
|
|
}
|
|
|
|
link = BidiChainGetNext(chain, link);
|
|
}
|
|
} else {
|
|
priorStrongType = isolatingRun->_sos;
|
|
}
|
|
|
|
/* Rule: N0.c.1 */
|
|
if (priorStrongType == oppositeDirection) {
|
|
pairType = oppositeDirection;
|
|
}
|
|
/* Rule: N0.c.2 */
|
|
else {
|
|
pairType = embeddingDirection;
|
|
}
|
|
}
|
|
/* Rule: N0.d */
|
|
else {
|
|
pairType = SBBidiTypeNil;
|
|
}
|
|
|
|
if (pairType != SBBidiTypeNil) {
|
|
/* Do the substitution */
|
|
BidiChainSetType(chain, openingLink, pairType);
|
|
BidiChainSetType(chain, closingLink, pairType);
|
|
}
|
|
}
|
|
|
|
BracketQueueDequeue(queue);
|
|
}
|
|
}
|
|
|
|
static void ResolveNeutrals(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
BidiLink roller = chain->roller;
|
|
BidiLink link;
|
|
|
|
SBLevel runLevel;
|
|
SBBidiType strongType;
|
|
BidiLink neutralLink;
|
|
|
|
runLevel = isolatingRun->baseLevelRun->level;
|
|
strongType = isolatingRun->_sos;
|
|
neutralLink = BidiLinkNone;
|
|
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
SBBidiType nextType;
|
|
|
|
SBAssert(SBBidiTypeIsStrongOrNumber(type) || SBBidiTypeIsNeutralOrIsolate(type));
|
|
|
|
switch (type) {
|
|
case SBBidiTypeL:
|
|
strongType = SBBidiTypeL;
|
|
break;
|
|
|
|
case SBBidiTypeR:
|
|
case SBBidiTypeEN:
|
|
case SBBidiTypeAN:
|
|
strongType = SBBidiTypeR;
|
|
break;
|
|
|
|
case SBBidiTypeB:
|
|
case SBBidiTypeS:
|
|
case SBBidiTypeWS:
|
|
case SBBidiTypeON:
|
|
case SBBidiTypeLRI:
|
|
case SBBidiTypeRLI:
|
|
case SBBidiTypeFSI:
|
|
case SBBidiTypePDI:
|
|
if (neutralLink == BidiLinkNone) {
|
|
neutralLink = link;
|
|
}
|
|
|
|
nextType = BidiChainGetType(chain, BidiChainGetNext(chain, link));
|
|
if (SBBidiTypeIsNumber(nextType)) {
|
|
nextType = SBBidiTypeR;
|
|
} else if (nextType == SBBidiTypeNil) {
|
|
nextType = isolatingRun->_eos;
|
|
}
|
|
|
|
if (SBBidiTypeIsStrong(nextType)) {
|
|
/* Rules N1, N2 */
|
|
SBBidiType resolvedType = (strongType == nextType
|
|
? strongType
|
|
: SBLevelAsNormalBidiType(runLevel));
|
|
|
|
do {
|
|
BidiChainSetType(chain, neutralLink, resolvedType);
|
|
neutralLink = BidiChainGetNext(chain, neutralLink);
|
|
} while (neutralLink != BidiChainGetNext(chain, link));
|
|
|
|
neutralLink = BidiLinkNone;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ResolveImplicitLevels(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiChainRef chain = isolatingRun->bidiChain;
|
|
BidiLink roller = chain->roller;
|
|
BidiLink link;
|
|
|
|
SBLevel runLevel = isolatingRun->baseLevelRun->level;
|
|
|
|
if ((runLevel & 1) == 0) {
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
SBLevel level = BidiChainGetLevel(chain, link);
|
|
|
|
SBAssert(SBBidiTypeIsStrongOrNumber(type));
|
|
|
|
/* Rule I1 */
|
|
if (type == SBBidiTypeR) {
|
|
BidiChainSetLevel(chain, link, level + 1);
|
|
} else if (type != SBBidiTypeL) {
|
|
BidiChainSetLevel(chain, link, level + 2);
|
|
}
|
|
}
|
|
} else {
|
|
BidiChainForEach(chain, roller, link) {
|
|
SBBidiType type = BidiChainGetType(chain, link);
|
|
SBLevel level = BidiChainGetLevel(chain, link);
|
|
|
|
SBAssert(SBBidiTypeIsStrongOrNumber(type));
|
|
|
|
/* Rule I2 */
|
|
if (type != SBBidiTypeR) {
|
|
BidiChainSetLevel(chain, link, level + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SB_INTERNAL void IsolatingRunInitialize(IsolatingRunRef isolatingRun)
|
|
{
|
|
BracketQueueInitialize(&isolatingRun->_bracketQueue);
|
|
}
|
|
|
|
SB_INTERNAL void IsolatingRunResolve(IsolatingRunRef isolatingRun)
|
|
{
|
|
BidiLink lastLink;
|
|
BidiLink subsequentLink;
|
|
|
|
SB_LOG_BLOCK_OPENER("Identified Isolating Run");
|
|
|
|
/* Attach level run links to form isolating run. */
|
|
AttachLevelRunLinks(isolatingRun);
|
|
/* Save last subsequent link. */
|
|
subsequentLink = isolatingRun->_lastLevelRun->subsequentLink;
|
|
|
|
SB_LOG_STATEMENT("Range", 1, SB_LOG_RUN_RANGE(isolatingRun));
|
|
SB_LOG_STATEMENT("Types", 1, SB_LOG_RUN_TYPES(isolatingRun));
|
|
SB_LOG_STATEMENT("Level", 1, SB_LOG_LEVEL(isolatingRun->baseLevelRun->level));
|
|
SB_LOG_STATEMENT("SOS", 1, SB_LOG_BIDI_TYPE(isolatingRun->_sos));
|
|
SB_LOG_STATEMENT("EOS", 1, SB_LOG_BIDI_TYPE(isolatingRun->_eos));
|
|
|
|
/* Rules W1-W7 */
|
|
lastLink = ResolveWeakTypes(isolatingRun);
|
|
SB_LOG_BLOCK_OPENER("Resolved Weak Types");
|
|
SB_LOG_STATEMENT("Types", 1, SB_LOG_RUN_TYPES(isolatingRun));
|
|
SB_LOG_BLOCK_CLOSER();
|
|
|
|
/* Rule N0 */
|
|
ResolveBrackets(isolatingRun);
|
|
SB_LOG_BLOCK_OPENER("Resolved Brackets");
|
|
SB_LOG_STATEMENT("Types", 1, SB_LOG_RUN_TYPES(isolatingRun));
|
|
SB_LOG_BLOCK_CLOSER();
|
|
|
|
/* Rules N1, N2 */
|
|
ResolveNeutrals(isolatingRun);
|
|
SB_LOG_BLOCK_OPENER("Resolved Neutrals");
|
|
SB_LOG_STATEMENT("Types", 1, SB_LOG_RUN_TYPES(isolatingRun));
|
|
SB_LOG_BLOCK_CLOSER();
|
|
|
|
/* Rules I1, I2 */
|
|
ResolveImplicitLevels(isolatingRun);
|
|
SB_LOG_BLOCK_OPENER("Resolved Implicit Levels");
|
|
SB_LOG_STATEMENT("Levels", 1, SB_LOG_RUN_LEVELS(isolatingRun));
|
|
SB_LOG_BLOCK_CLOSER();
|
|
|
|
/* Re-attach original links. */
|
|
AttachOriginalLinks(isolatingRun);
|
|
/* Attach new final link (of isolating run) with last subsequent link. */
|
|
BidiChainSetNext(isolatingRun->bidiChain, lastLink, subsequentLink);
|
|
|
|
SB_LOG_BLOCK_CLOSER();
|
|
}
|
|
|
|
SB_INTERNAL void IsolatingRunFinalize(IsolatingRunRef isolatingRun)
|
|
{
|
|
BracketQueueFinalize(&isolatingRun->_bracketQueue);
|
|
}
|