| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ********************************************************************** |
| * Copyright (c) 2002-2016, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ********************************************************************** |
| */ |
| // |
| // rbbitblb.cpp |
| // |
| |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_BREAK_ITERATION |
| |
| #if defined(STARBOARD) |
| #include "starboard/client_porting/poem/assert_poem.h" |
| #include "starboard/client_porting/poem/string_poem.h" |
| #endif // defined(STARBOARD) |
| #include "unicode/unistr.h" |
| #include "rbbitblb.h" |
| #include "rbbirb.h" |
| #include "rbbiscan.h" |
| #include "rbbisetb.h" |
| #include "rbbidata.h" |
| #include "cstring.h" |
| #include "uassert.h" |
| #include "uvectr32.h" |
| #include "cmemory.h" |
| |
| U_NAMESPACE_BEGIN |
| |
| const int32_t kMaxStateFor8BitsTable = 255; |
| |
| RBBITableBuilder::RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status) : |
| fRB(rb), |
| fTree(*rootNode), |
| fStatus(&status), |
| fDStates(nullptr), |
| fSafeTable(nullptr) { |
| if (U_FAILURE(status)) { |
| return; |
| } |
| // fDStates is UVector<RBBIStateDescriptor *> |
| fDStates = new UVector(status); |
| if (U_SUCCESS(status) && fDStates == nullptr ) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| } |
| } |
| |
| |
| |
| RBBITableBuilder::~RBBITableBuilder() { |
| int i; |
| for (i=0; i<fDStates->size(); i++) { |
| delete (RBBIStateDescriptor *)fDStates->elementAt(i); |
| } |
| delete fDStates; |
| delete fSafeTable; |
| delete fLookAheadRuleMap; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // RBBITableBuilder::buildForwardTable - This is the main function for building |
| // the DFA state transition table from the RBBI rules parse tree. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::buildForwardTable() { |
| |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| // If there were no rules, just return. This situation can easily arise |
| // for the reverse rules. |
| if (fTree==NULL) { |
| return; |
| } |
| |
| // |
| // Walk through the tree, replacing any references to $variables with a copy of the |
| // parse tree for the substition expression. |
| // |
| fTree = fTree->flattenVariables(); |
| #ifdef RBBI_DEBUG |
| if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "ftree")) { |
| RBBIDebugPuts("\nParse tree after flattening variable references."); |
| RBBINode::printTree(fTree, TRUE); |
| } |
| #endif |
| |
| // |
| // If the rules contained any references to {bof} |
| // add a {bof} <cat> <former root of tree> to the |
| // tree. Means that all matches must start out with the |
| // {bof} fake character. |
| // |
| if (fRB->fSetBuilder->sawBOF()) { |
| RBBINode *bofTop = new RBBINode(RBBINode::opCat); |
| RBBINode *bofLeaf = new RBBINode(RBBINode::leafChar); |
| // Delete and exit if memory allocation failed. |
| if (bofTop == NULL || bofLeaf == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| delete bofTop; |
| delete bofLeaf; |
| return; |
| } |
| bofTop->fLeftChild = bofLeaf; |
| bofTop->fRightChild = fTree; |
| bofLeaf->fParent = bofTop; |
| bofLeaf->fVal = 2; // Reserved value for {bof}. |
| fTree = bofTop; |
| } |
| |
| // |
| // Add a unique right-end marker to the expression. |
| // Appears as a cat-node, left child being the original tree, |
| // right child being the end marker. |
| // |
| RBBINode *cn = new RBBINode(RBBINode::opCat); |
| // Exit if memory allocation failed. |
| if (cn == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| cn->fLeftChild = fTree; |
| fTree->fParent = cn; |
| RBBINode *endMarkerNode = cn->fRightChild = new RBBINode(RBBINode::endMark); |
| // Delete and exit if memory allocation failed. |
| if (cn->fRightChild == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| delete cn; |
| return; |
| } |
| cn->fRightChild->fParent = cn; |
| fTree = cn; |
| |
| // |
| // Replace all references to UnicodeSets with the tree for the equivalent |
| // expression. |
| // |
| fTree->flattenSets(); |
| #ifdef RBBI_DEBUG |
| if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "stree")) { |
| RBBIDebugPuts("\nParse tree after flattening Unicode Set references."); |
| RBBINode::printTree(fTree, TRUE); |
| } |
| #endif |
| |
| |
| // |
| // calculate the functions nullable, firstpos, lastpos and followpos on |
| // nodes in the parse tree. |
| // See the alogrithm description in Aho. |
| // Understanding how this works by looking at the code alone will be |
| // nearly impossible. |
| // |
| calcNullable(fTree); |
| calcFirstPos(fTree); |
| calcLastPos(fTree); |
| calcFollowPos(fTree); |
| if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "pos")) { |
| RBBIDebugPuts("\n"); |
| printPosSets(fTree); |
| } |
| |
| // |
| // For "chained" rules, modify the followPos sets |
| // |
| if (fRB->fChainRules) { |
| calcChainedFollowPos(fTree, endMarkerNode); |
| } |
| |
| // |
| // BOF (start of input) test fixup. |
| // |
| if (fRB->fSetBuilder->sawBOF()) { |
| bofFixup(); |
| } |
| |
| // |
| // Build the DFA state transition tables. |
| // |
| buildStateTable(); |
| mapLookAheadRules(); |
| flagAcceptingStates(); |
| flagLookAheadStates(); |
| flagTaggedStates(); |
| |
| // |
| // Update the global table of rule status {tag} values |
| // The rule builder has a global vector of status values that are common |
| // for all tables. Merge the ones from this table into the global set. |
| // |
| mergeRuleStatusVals(); |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // calcNullable. Impossible to explain succinctly. See Aho, section 3.9 |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::calcNullable(RBBINode *n) { |
| if (n == NULL) { |
| return; |
| } |
| if (n->fType == RBBINode::setRef || |
| n->fType == RBBINode::endMark ) { |
| // These are non-empty leaf node types. |
| n->fNullable = FALSE; |
| return; |
| } |
| |
| if (n->fType == RBBINode::lookAhead || n->fType == RBBINode::tag) { |
| // Lookahead marker node. It's a leaf, so no recursion on children. |
| // It's nullable because it does not match any literal text from the input stream. |
| n->fNullable = TRUE; |
| return; |
| } |
| |
| |
| // The node is not a leaf. |
| // Calculate nullable on its children. |
| calcNullable(n->fLeftChild); |
| calcNullable(n->fRightChild); |
| |
| // Apply functions from table 3.40 in Aho |
| if (n->fType == RBBINode::opOr) { |
| n->fNullable = n->fLeftChild->fNullable || n->fRightChild->fNullable; |
| } |
| else if (n->fType == RBBINode::opCat) { |
| n->fNullable = n->fLeftChild->fNullable && n->fRightChild->fNullable; |
| } |
| else if (n->fType == RBBINode::opStar || n->fType == RBBINode::opQuestion) { |
| n->fNullable = TRUE; |
| } |
| else { |
| n->fNullable = FALSE; |
| } |
| } |
| |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // calcFirstPos. Impossible to explain succinctly. See Aho, section 3.9 |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::calcFirstPos(RBBINode *n) { |
| if (n == NULL) { |
| return; |
| } |
| if (n->fType == RBBINode::leafChar || |
| n->fType == RBBINode::endMark || |
| n->fType == RBBINode::lookAhead || |
| n->fType == RBBINode::tag) { |
| // These are non-empty leaf node types. |
| // Note: In order to maintain the sort invariant on the set, |
| // this function should only be called on a node whose set is |
| // empty to start with. |
| n->fFirstPosSet->addElement(n, *fStatus); |
| return; |
| } |
| |
| // The node is not a leaf. |
| // Calculate firstPos on its children. |
| calcFirstPos(n->fLeftChild); |
| calcFirstPos(n->fRightChild); |
| |
| // Apply functions from table 3.40 in Aho |
| if (n->fType == RBBINode::opOr) { |
| setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); |
| setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); |
| } |
| else if (n->fType == RBBINode::opCat) { |
| setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); |
| if (n->fLeftChild->fNullable) { |
| setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); |
| } |
| } |
| else if (n->fType == RBBINode::opStar || |
| n->fType == RBBINode::opQuestion || |
| n->fType == RBBINode::opPlus) { |
| setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); |
| } |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // calcLastPos. Impossible to explain succinctly. See Aho, section 3.9 |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::calcLastPos(RBBINode *n) { |
| if (n == NULL) { |
| return; |
| } |
| if (n->fType == RBBINode::leafChar || |
| n->fType == RBBINode::endMark || |
| n->fType == RBBINode::lookAhead || |
| n->fType == RBBINode::tag) { |
| // These are non-empty leaf node types. |
| // Note: In order to maintain the sort invariant on the set, |
| // this function should only be called on a node whose set is |
| // empty to start with. |
| n->fLastPosSet->addElement(n, *fStatus); |
| return; |
| } |
| |
| // The node is not a leaf. |
| // Calculate lastPos on its children. |
| calcLastPos(n->fLeftChild); |
| calcLastPos(n->fRightChild); |
| |
| // Apply functions from table 3.40 in Aho |
| if (n->fType == RBBINode::opOr) { |
| setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); |
| setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); |
| } |
| else if (n->fType == RBBINode::opCat) { |
| setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); |
| if (n->fRightChild->fNullable) { |
| setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); |
| } |
| } |
| else if (n->fType == RBBINode::opStar || |
| n->fType == RBBINode::opQuestion || |
| n->fType == RBBINode::opPlus) { |
| setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); |
| } |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // calcFollowPos. Impossible to explain succinctly. See Aho, section 3.9 |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::calcFollowPos(RBBINode *n) { |
| if (n == NULL || |
| n->fType == RBBINode::leafChar || |
| n->fType == RBBINode::endMark) { |
| return; |
| } |
| |
| calcFollowPos(n->fLeftChild); |
| calcFollowPos(n->fRightChild); |
| |
| // Aho rule #1 |
| if (n->fType == RBBINode::opCat) { |
| RBBINode *i; // is 'i' in Aho's description |
| uint32_t ix; |
| |
| UVector *LastPosOfLeftChild = n->fLeftChild->fLastPosSet; |
| |
| for (ix=0; ix<(uint32_t)LastPosOfLeftChild->size(); ix++) { |
| i = (RBBINode *)LastPosOfLeftChild->elementAt(ix); |
| setAdd(i->fFollowPos, n->fRightChild->fFirstPosSet); |
| } |
| } |
| |
| // Aho rule #2 |
| if (n->fType == RBBINode::opStar || |
| n->fType == RBBINode::opPlus) { |
| RBBINode *i; // again, n and i are the names from Aho's description. |
| uint32_t ix; |
| |
| for (ix=0; ix<(uint32_t)n->fLastPosSet->size(); ix++) { |
| i = (RBBINode *)n->fLastPosSet->elementAt(ix); |
| setAdd(i->fFollowPos, n->fFirstPosSet); |
| } |
| } |
| |
| |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // addRuleRootNodes Recursively walk a parse tree, adding all nodes flagged |
| // as roots of a rule to a destination vector. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::addRuleRootNodes(UVector *dest, RBBINode *node) { |
| if (node == NULL || U_FAILURE(*fStatus)) { |
| return; |
| } |
| if (node->fRuleRoot) { |
| dest->addElement(node, *fStatus); |
| // Note: rules cannot nest. If we found a rule start node, |
| // no child node can also be a start node. |
| return; |
| } |
| addRuleRootNodes(dest, node->fLeftChild); |
| addRuleRootNodes(dest, node->fRightChild); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // calcChainedFollowPos. Modify the previously calculated followPos sets |
| // to implement rule chaining. NOT described by Aho |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::calcChainedFollowPos(RBBINode *tree, RBBINode *endMarkNode) { |
| |
| UVector leafNodes(*fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| // get a list all leaf nodes |
| tree->findNodes(&leafNodes, RBBINode::leafChar, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| // Collect all leaf nodes that can start matches for rules |
| // with inbound chaining enabled, which is the union of the |
| // firstPosition sets from each of the rule root nodes. |
| |
| UVector ruleRootNodes(*fStatus); |
| addRuleRootNodes(&ruleRootNodes, tree); |
| |
| UVector matchStartNodes(*fStatus); |
| for (int j=0; j<ruleRootNodes.size(); ++j) { |
| RBBINode *node = static_cast<RBBINode *>(ruleRootNodes.elementAt(j)); |
| if (node->fChainIn) { |
| setAdd(&matchStartNodes, node->fFirstPosSet); |
| } |
| } |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| int32_t endNodeIx; |
| int32_t startNodeIx; |
| |
| for (endNodeIx=0; endNodeIx<leafNodes.size(); endNodeIx++) { |
| RBBINode *endNode = (RBBINode *)leafNodes.elementAt(endNodeIx); |
| |
| // Identify leaf nodes that correspond to overall rule match positions. |
| // These include the endMarkNode in their followPos sets. |
| // |
| // Note: do not consider other end marker nodes, those that are added to |
| // look-ahead rules. These can't chain; a match immediately stops |
| // further matching. This leaves exactly one end marker node, the one |
| // at the end of the complete tree. |
| |
| if (!endNode->fFollowPos->contains(endMarkNode)) { |
| continue; |
| } |
| |
| // We've got a node that can end a match. |
| |
| // !!LBCMNoChain implementation: If this node's val correspond to |
| // the Line Break $CM char class, don't chain from it. |
| // TODO: Remove this. !!LBCMNoChain is deprecated, and is not used |
| // by any of the standard ICU rules. |
| if (fRB->fLBCMNoChain) { |
| UChar32 c = this->fRB->fSetBuilder->getFirstChar(endNode->fVal); |
| if (c != -1) { |
| // c == -1 occurs with sets containing only the {eof} marker string. |
| ULineBreak cLBProp = (ULineBreak)u_getIntPropertyValue(c, UCHAR_LINE_BREAK); |
| if (cLBProp == U_LB_COMBINING_MARK) { |
| continue; |
| } |
| } |
| } |
| |
| // Now iterate over the nodes that can start a match, looking for ones |
| // with the same char class as our ending node. |
| RBBINode *startNode; |
| for (startNodeIx = 0; startNodeIx<matchStartNodes.size(); startNodeIx++) { |
| startNode = (RBBINode *)matchStartNodes.elementAt(startNodeIx); |
| if (startNode->fType != RBBINode::leafChar) { |
| continue; |
| } |
| |
| if (endNode->fVal == startNode->fVal) { |
| // The end val (character class) of one possible match is the |
| // same as the start of another. |
| |
| // Add all nodes from the followPos of the start node to the |
| // followPos set of the end node, which will have the effect of |
| // letting matches transition from a match state at endNode |
| // to the second char of a match starting with startNode. |
| setAdd(endNode->fFollowPos, startNode->fFollowPos); |
| } |
| } |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // bofFixup. Fixup for state tables that include {bof} beginning of input testing. |
| // Do an swizzle similar to chaining, modifying the followPos set of |
| // the bofNode to include the followPos nodes from other {bot} nodes |
| // scattered through the tree. |
| // |
| // This function has much in common with calcChainedFollowPos(). |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::bofFixup() { |
| |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| // The parse tree looks like this ... |
| // fTree root ---> <cat> |
| // / \ . |
| // <cat> <#end node> |
| // / \ . |
| // <bofNode> rest |
| // of tree |
| // |
| // We will be adding things to the followPos set of the <bofNode> |
| // |
| RBBINode *bofNode = fTree->fLeftChild->fLeftChild; |
| U_ASSERT(bofNode->fType == RBBINode::leafChar); |
| U_ASSERT(bofNode->fVal == 2); |
| |
| // Get all nodes that can be the start a match of the user-written rules |
| // (excluding the fake bofNode) |
| // We want the nodes that can start a match in the |
| // part labeled "rest of tree" |
| // |
| UVector *matchStartNodes = fTree->fLeftChild->fRightChild->fFirstPosSet; |
| |
| RBBINode *startNode; |
| int startNodeIx; |
| for (startNodeIx = 0; startNodeIx<matchStartNodes->size(); startNodeIx++) { |
| startNode = (RBBINode *)matchStartNodes->elementAt(startNodeIx); |
| if (startNode->fType != RBBINode::leafChar) { |
| continue; |
| } |
| |
| if (startNode->fVal == bofNode->fVal) { |
| // We found a leaf node corresponding to a {bof} that was |
| // explicitly written into a rule. |
| // Add everything from the followPos set of this node to the |
| // followPos set of the fake bofNode at the start of the tree. |
| // |
| setAdd(bofNode->fFollowPos, startNode->fFollowPos); |
| } |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // buildStateTable() Determine the set of runtime DFA states and the |
| // transition tables for these states, by the algorithm |
| // of fig. 3.44 in Aho. |
| // |
| // Most of the comments are quotes of Aho's psuedo-code. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::buildStateTable() { |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| RBBIStateDescriptor *failState; |
| // Set it to NULL to avoid uninitialized warning |
| RBBIStateDescriptor *initialState = NULL; |
| // |
| // Add a dummy state 0 - the stop state. Not from Aho. |
| int lastInputSymbol = fRB->fSetBuilder->getNumCharCategories() - 1; |
| failState = new RBBIStateDescriptor(lastInputSymbol, fStatus); |
| if (failState == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| goto ExitBuildSTdeleteall; |
| } |
| failState->fPositions = new UVector(*fStatus); |
| if (failState->fPositions == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| } |
| if (failState->fPositions == NULL || U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| fDStates->addElement(failState, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| |
| // initially, the only unmarked state in Dstates is firstpos(root), |
| // where toot is the root of the syntax tree for (r)#; |
| initialState = new RBBIStateDescriptor(lastInputSymbol, fStatus); |
| if (initialState == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| } |
| if (U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| initialState->fPositions = new UVector(*fStatus); |
| if (initialState->fPositions == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| } |
| if (U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| setAdd(initialState->fPositions, fTree->fFirstPosSet); |
| fDStates->addElement(initialState, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| |
| // while there is an unmarked state T in Dstates do begin |
| for (;;) { |
| RBBIStateDescriptor *T = NULL; |
| int32_t tx; |
| for (tx=1; tx<fDStates->size(); tx++) { |
| RBBIStateDescriptor *temp; |
| temp = (RBBIStateDescriptor *)fDStates->elementAt(tx); |
| if (temp->fMarked == FALSE) { |
| T = temp; |
| break; |
| } |
| } |
| if (T == NULL) { |
| break; |
| } |
| |
| // mark T; |
| T->fMarked = TRUE; |
| |
| // for each input symbol a do begin |
| int32_t a; |
| for (a = 1; a<=lastInputSymbol; a++) { |
| // let U be the set of positions that are in followpos(p) |
| // for some position p in T |
| // such that the symbol at position p is a; |
| UVector *U = NULL; |
| RBBINode *p; |
| int32_t px; |
| for (px=0; px<T->fPositions->size(); px++) { |
| p = (RBBINode *)T->fPositions->elementAt(px); |
| if ((p->fType == RBBINode::leafChar) && (p->fVal == a)) { |
| if (U == NULL) { |
| U = new UVector(*fStatus); |
| if (U == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| goto ExitBuildSTdeleteall; |
| } |
| } |
| setAdd(U, p->fFollowPos); |
| } |
| } |
| |
| // if U is not empty and not in DStates then |
| int32_t ux = 0; |
| UBool UinDstates = FALSE; |
| if (U != NULL) { |
| U_ASSERT(U->size() > 0); |
| int ix; |
| for (ix=0; ix<fDStates->size(); ix++) { |
| RBBIStateDescriptor *temp2; |
| temp2 = (RBBIStateDescriptor *)fDStates->elementAt(ix); |
| if (setEquals(U, temp2->fPositions)) { |
| delete U; |
| U = temp2->fPositions; |
| ux = ix; |
| UinDstates = TRUE; |
| break; |
| } |
| } |
| |
| // Add U as an unmarked state to Dstates |
| if (!UinDstates) |
| { |
| RBBIStateDescriptor *newState = new RBBIStateDescriptor(lastInputSymbol, fStatus); |
| if (newState == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| } |
| if (U_FAILURE(*fStatus)) { |
| goto ExitBuildSTdeleteall; |
| } |
| newState->fPositions = U; |
| fDStates->addElement(newState, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| ux = fDStates->size()-1; |
| } |
| |
| // Dtran[T, a] := U; |
| T->fDtran->setElementAt(ux, a); |
| } |
| } |
| } |
| return; |
| // delete local pointers only if error occured. |
| ExitBuildSTdeleteall: |
| delete initialState; |
| delete failState; |
| } |
| |
| |
| /** |
| * mapLookAheadRules |
| * |
| */ |
| void RBBITableBuilder::mapLookAheadRules() { |
| fLookAheadRuleMap = new UVector32(fRB->fScanner->numRules() + 1, *fStatus); |
| if (fLookAheadRuleMap == nullptr) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| } |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| fLookAheadRuleMap->setSize(fRB->fScanner->numRules() + 1); |
| |
| for (int32_t n=0; n<fDStates->size(); n++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| int32_t laSlotForState = 0; |
| |
| // Establish the look-ahead slot for this state, if the state covers |
| // any look-ahead nodes - corresponding to the '/' in look-ahead rules. |
| |
| // If any of the look-ahead nodes already have a slot assigned, use it, |
| // otherwise assign a new one. |
| |
| bool sawLookAheadNode = false; |
| for (int32_t ipos=0; ipos<sd->fPositions->size(); ++ipos) { |
| RBBINode *node = static_cast<RBBINode *>(sd->fPositions->elementAt(ipos)); |
| if (node->fType != RBBINode::NodeType::lookAhead) { |
| continue; |
| } |
| sawLookAheadNode = true; |
| int32_t ruleNum = node->fVal; // Set when rule was originally parsed. |
| U_ASSERT(ruleNum < fLookAheadRuleMap->size()); |
| U_ASSERT(ruleNum > 0); |
| int32_t laSlot = fLookAheadRuleMap->elementAti(ruleNum); |
| if (laSlot != 0) { |
| if (laSlotForState == 0) { |
| laSlotForState = laSlot; |
| } else { |
| // TODO: figure out if this can fail, change to setting an error code if so. |
| U_ASSERT(laSlot == laSlotForState); |
| } |
| } |
| } |
| if (!sawLookAheadNode) { |
| continue; |
| } |
| |
| if (laSlotForState == 0) { |
| laSlotForState = ++fLASlotsInUse; |
| } |
| |
| // For each look ahead node covered by this state, |
| // set the mapping from the node's rule number to the look ahead slot. |
| // There can be multiple nodes/rule numbers going to the same la slot. |
| |
| for (int32_t ipos=0; ipos<sd->fPositions->size(); ++ipos) { |
| RBBINode *node = static_cast<RBBINode *>(sd->fPositions->elementAt(ipos)); |
| if (node->fType != RBBINode::NodeType::lookAhead) { |
| continue; |
| } |
| int32_t ruleNum = node->fVal; // Set when rule was originally parsed. |
| int32_t existingVal = fLookAheadRuleMap->elementAti(ruleNum); |
| (void)existingVal; |
| U_ASSERT(existingVal == 0 || existingVal == laSlotForState); |
| fLookAheadRuleMap->setElementAt(laSlotForState, ruleNum); |
| } |
| } |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // flagAcceptingStates Identify accepting states. |
| // First get a list of all of the end marker nodes. |
| // Then, for each state s, |
| // if s contains one of the end marker nodes in its list of tree positions then |
| // s is an accepting state. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::flagAcceptingStates() { |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| UVector endMarkerNodes(*fStatus); |
| RBBINode *endMarker; |
| int32_t i; |
| int32_t n; |
| |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| fTree->findNodes(&endMarkerNodes, RBBINode::endMark, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| |
| for (i=0; i<endMarkerNodes.size(); i++) { |
| endMarker = (RBBINode *)endMarkerNodes.elementAt(i); |
| for (n=0; n<fDStates->size(); n++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| if (sd->fPositions->indexOf(endMarker) >= 0) { |
| // Any non-zero value for fAccepting means this is an accepting node. |
| // The value is what will be returned to the user as the break status. |
| // If no other value was specified, force it to ACCEPTING_UNCONDITIONAL (1). |
| |
| if (sd->fAccepting==0) { |
| // State hasn't been marked as accepting yet. Do it now. |
| sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); |
| if (sd->fAccepting == 0) { |
| sd->fAccepting = ACCEPTING_UNCONDITIONAL; |
| } |
| } |
| if (sd->fAccepting==ACCEPTING_UNCONDITIONAL && endMarker->fVal != 0) { |
| // Both lookahead and non-lookahead accepting for this state. |
| // Favor the look-ahead, because a look-ahead match needs to |
| // immediately stop the run-time engine. First match, not longest. |
| sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); |
| } |
| // implicit else: |
| // if sd->fAccepting already had a value other than 0 or 1, leave it be. |
| } |
| } |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // flagLookAheadStates Very similar to flagAcceptingStates, above. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::flagLookAheadStates() { |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| UVector lookAheadNodes(*fStatus); |
| RBBINode *lookAheadNode; |
| int32_t i; |
| int32_t n; |
| |
| fTree->findNodes(&lookAheadNodes, RBBINode::lookAhead, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| for (i=0; i<lookAheadNodes.size(); i++) { |
| lookAheadNode = (RBBINode *)lookAheadNodes.elementAt(i); |
| U_ASSERT(lookAheadNode->fType == RBBINode::NodeType::lookAhead); |
| |
| for (n=0; n<fDStates->size(); n++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| int32_t positionsIdx = sd->fPositions->indexOf(lookAheadNode); |
| if (positionsIdx >= 0) { |
| U_ASSERT(lookAheadNode == sd->fPositions->elementAt(positionsIdx)); |
| uint32_t lookaheadSlot = fLookAheadRuleMap->elementAti(lookAheadNode->fVal); |
| U_ASSERT(sd->fLookAhead == 0 || sd->fLookAhead == lookaheadSlot); |
| // if (sd->fLookAhead != 0 && sd->fLookAhead != lookaheadSlot) { |
| // printf("%s:%d Bingo. sd->fLookAhead:%d lookaheadSlot:%d\n", |
| // __FILE__, __LINE__, sd->fLookAhead, lookaheadSlot); |
| // } |
| sd->fLookAhead = lookaheadSlot; |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // flagTaggedStates |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::flagTaggedStates() { |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| UVector tagNodes(*fStatus); |
| RBBINode *tagNode; |
| int32_t i; |
| int32_t n; |
| |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| fTree->findNodes(&tagNodes, RBBINode::tag, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| for (i=0; i<tagNodes.size(); i++) { // For each tag node t (all of 'em) |
| tagNode = (RBBINode *)tagNodes.elementAt(i); |
| |
| for (n=0; n<fDStates->size(); n++) { // For each state s (row in the state table) |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| if (sd->fPositions->indexOf(tagNode) >= 0) { // if s include the tag node t |
| sortedAdd(&sd->fTagVals, tagNode->fVal); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // mergeRuleStatusVals |
| // |
| // Update the global table of rule status {tag} values |
| // The rule builder has a global vector of status values that are common |
| // for all tables. Merge the ones from this table into the global set. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::mergeRuleStatusVals() { |
| // |
| // The basic outline of what happens here is this... |
| // |
| // for each state in this state table |
| // if the status tag list for this state is in the global statuses list |
| // record where and |
| // continue with the next state |
| // else |
| // add the tag list for this state to the global list. |
| // |
| int i; |
| int n; |
| |
| // Pre-set a single tag of {0} into the table. |
| // We will need this as a default, for rule sets with no explicit tagging. |
| if (fRB->fRuleStatusVals->size() == 0) { |
| fRB->fRuleStatusVals->addElement(1, *fStatus); // Num of statuses in group |
| fRB->fRuleStatusVals->addElement((int32_t)0, *fStatus); // and our single status of zero |
| } |
| |
| // For each state |
| for (n=0; n<fDStates->size(); n++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| UVector *thisStatesTagValues = sd->fTagVals; |
| if (thisStatesTagValues == NULL) { |
| // No tag values are explicitly associated with this state. |
| // Set the default tag value. |
| sd->fTagsIdx = 0; |
| continue; |
| } |
| |
| // There are tag(s) associated with this state. |
| // fTagsIdx will be the index into the global tag list for this state's tag values. |
| // Initial value of -1 flags that we haven't got it set yet. |
| sd->fTagsIdx = -1; |
| int32_t thisTagGroupStart = 0; // indexes into the global rule status vals list |
| int32_t nextTagGroupStart = 0; |
| |
| // Loop runs once per group of tags in the global list |
| while (nextTagGroupStart < fRB->fRuleStatusVals->size()) { |
| thisTagGroupStart = nextTagGroupStart; |
| nextTagGroupStart += fRB->fRuleStatusVals->elementAti(thisTagGroupStart) + 1; |
| if (thisStatesTagValues->size() != fRB->fRuleStatusVals->elementAti(thisTagGroupStart)) { |
| // The number of tags for this state is different from |
| // the number of tags in this group from the global list. |
| // Continue with the next group from the global list. |
| continue; |
| } |
| // The lengths match, go ahead and compare the actual tag values |
| // between this state and the group from the global list. |
| for (i=0; i<thisStatesTagValues->size(); i++) { |
| if (thisStatesTagValues->elementAti(i) != |
| fRB->fRuleStatusVals->elementAti(thisTagGroupStart + 1 + i) ) { |
| // Mismatch. |
| break; |
| } |
| } |
| |
| if (i == thisStatesTagValues->size()) { |
| // We found a set of tag values in the global list that match |
| // those for this state. Use them. |
| sd->fTagsIdx = thisTagGroupStart; |
| break; |
| } |
| } |
| |
| if (sd->fTagsIdx == -1) { |
| // No suitable entry in the global tag list already. Add one |
| sd->fTagsIdx = fRB->fRuleStatusVals->size(); |
| fRB->fRuleStatusVals->addElement(thisStatesTagValues->size(), *fStatus); |
| for (i=0; i<thisStatesTagValues->size(); i++) { |
| fRB->fRuleStatusVals->addElement(thisStatesTagValues->elementAti(i), *fStatus); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // sortedAdd Add a value to a vector of sorted values (ints). |
| // Do not replicate entries; if the value is already there, do not |
| // add a second one. |
| // Lazily create the vector if it does not already exist. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::sortedAdd(UVector **vector, int32_t val) { |
| int32_t i; |
| |
| if (*vector == NULL) { |
| *vector = new UVector(*fStatus); |
| } |
| if (*vector == NULL || U_FAILURE(*fStatus)) { |
| return; |
| } |
| UVector *vec = *vector; |
| int32_t vSize = vec->size(); |
| for (i=0; i<vSize; i++) { |
| int32_t valAtI = vec->elementAti(i); |
| if (valAtI == val) { |
| // The value is already in the vector. Don't add it again. |
| return; |
| } |
| if (valAtI > val) { |
| break; |
| } |
| } |
| vec->insertElementAt(val, i, *fStatus); |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // setAdd Set operation on UVector |
| // dest = dest union source |
| // Elements may only appear once and must be sorted. |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::setAdd(UVector *dest, UVector *source) { |
| int32_t destOriginalSize = dest->size(); |
| int32_t sourceSize = source->size(); |
| int32_t di = 0; |
| MaybeStackArray<void *, 16> destArray, sourceArray; // Handle small cases without malloc |
| void **destPtr, **sourcePtr; |
| void **destLim, **sourceLim; |
| |
| if (destOriginalSize > destArray.getCapacity()) { |
| if (destArray.resize(destOriginalSize) == NULL) { |
| return; |
| } |
| } |
| destPtr = destArray.getAlias(); |
| destLim = destPtr + destOriginalSize; // destArray.getArrayLimit()? |
| |
| if (sourceSize > sourceArray.getCapacity()) { |
| if (sourceArray.resize(sourceSize) == NULL) { |
| return; |
| } |
| } |
| sourcePtr = sourceArray.getAlias(); |
| sourceLim = sourcePtr + sourceSize; // sourceArray.getArrayLimit()? |
| |
| // Avoid multiple "get element" calls by getting the contents into arrays |
| (void) dest->toArray(destPtr); |
| (void) source->toArray(sourcePtr); |
| |
| dest->setSize(sourceSize+destOriginalSize, *fStatus); |
| |
| while (sourcePtr < sourceLim && destPtr < destLim) { |
| if (*destPtr == *sourcePtr) { |
| dest->setElementAt(*sourcePtr++, di++); |
| destPtr++; |
| } |
| // This check is required for machines with segmented memory, like i5/OS. |
| // Direct pointer comparison is not recommended. |
| else if (uprv_memcmp(destPtr, sourcePtr, sizeof(void *)) < 0) { |
| dest->setElementAt(*destPtr++, di++); |
| } |
| else { /* *sourcePtr < *destPtr */ |
| dest->setElementAt(*sourcePtr++, di++); |
| } |
| } |
| |
| // At most one of these two cleanup loops will execute |
| while (destPtr < destLim) { |
| dest->setElementAt(*destPtr++, di++); |
| } |
| while (sourcePtr < sourceLim) { |
| dest->setElementAt(*sourcePtr++, di++); |
| } |
| |
| dest->setSize(di, *fStatus); |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // setEqual Set operation on UVector. |
| // Compare for equality. |
| // Elements must be sorted. |
| // |
| //----------------------------------------------------------------------------- |
| UBool RBBITableBuilder::setEquals(UVector *a, UVector *b) { |
| return a->equals(*b); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // printPosSets Debug function. Dump Nullable, firstpos, lastpos and followpos |
| // for each node in the tree. |
| // |
| //----------------------------------------------------------------------------- |
| #ifdef RBBI_DEBUG |
| void RBBITableBuilder::printPosSets(RBBINode *n) { |
| if (n==NULL) { |
| return; |
| } |
| printf("\n"); |
| RBBINode::printNodeHeader(); |
| RBBINode::printNode(n); |
| RBBIDebugPrintf(" Nullable: %s\n", n->fNullable?"TRUE":"FALSE"); |
| |
| RBBIDebugPrintf(" firstpos: "); |
| printSet(n->fFirstPosSet); |
| |
| RBBIDebugPrintf(" lastpos: "); |
| printSet(n->fLastPosSet); |
| |
| RBBIDebugPrintf(" followpos: "); |
| printSet(n->fFollowPos); |
| |
| printPosSets(n->fLeftChild); |
| printPosSets(n->fRightChild); |
| } |
| #endif |
| |
| // |
| // findDuplCharClassFrom() |
| // |
| bool RBBITableBuilder::findDuplCharClassFrom(IntPair *categories) { |
| int32_t numStates = fDStates->size(); |
| int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); |
| |
| for (; categories->first < numCols-1; categories->first++) { |
| // Note: dictionary & non-dictionary columns cannot be merged. |
| // The limitSecond value prevents considering mixed pairs. |
| // Dictionary categories are >= DictCategoriesStart. |
| // Non dict categories are < DictCategoriesStart. |
| int limitSecond = categories->first < fRB->fSetBuilder->getDictCategoriesStart() ? |
| fRB->fSetBuilder->getDictCategoriesStart() : numCols; |
| for (categories->second=categories->first+1; categories->second < limitSecond; categories->second++) { |
| // Initialized to different values to prevent returning true if numStates = 0 (implies no duplicates). |
| uint16_t table_base = 0; |
| uint16_t table_dupl = 1; |
| for (int32_t state=0; state<numStates; state++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); |
| table_base = (uint16_t)sd->fDtran->elementAti(categories->first); |
| table_dupl = (uint16_t)sd->fDtran->elementAti(categories->second); |
| if (table_base != table_dupl) { |
| break; |
| } |
| } |
| if (table_base == table_dupl) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| // |
| // removeColumn() |
| // |
| void RBBITableBuilder::removeColumn(int32_t column) { |
| int32_t numStates = fDStates->size(); |
| for (int32_t state=0; state<numStates; state++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); |
| U_ASSERT(column < sd->fDtran->size()); |
| sd->fDtran->removeElementAt(column); |
| } |
| } |
| |
| /* |
| * findDuplicateState |
| */ |
| bool RBBITableBuilder::findDuplicateState(IntPair *states) { |
| int32_t numStates = fDStates->size(); |
| int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); |
| |
| for (; states->first<numStates-1; states->first++) { |
| RBBIStateDescriptor *firstSD = (RBBIStateDescriptor *)fDStates->elementAt(states->first); |
| for (states->second=states->first+1; states->second<numStates; states->second++) { |
| RBBIStateDescriptor *duplSD = (RBBIStateDescriptor *)fDStates->elementAt(states->second); |
| if (firstSD->fAccepting != duplSD->fAccepting || |
| firstSD->fLookAhead != duplSD->fLookAhead || |
| firstSD->fTagsIdx != duplSD->fTagsIdx) { |
| continue; |
| } |
| bool rowsMatch = true; |
| for (int32_t col=0; col < numCols; ++col) { |
| int32_t firstVal = firstSD->fDtran->elementAti(col); |
| int32_t duplVal = duplSD->fDtran->elementAti(col); |
| if (!((firstVal == duplVal) || |
| ((firstVal == states->first || firstVal == states->second) && |
| (duplVal == states->first || duplVal == states->second)))) { |
| rowsMatch = false; |
| break; |
| } |
| } |
| if (rowsMatch) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| bool RBBITableBuilder::findDuplicateSafeState(IntPair *states) { |
| int32_t numStates = fSafeTable->size(); |
| |
| for (; states->first<numStates-1; states->first++) { |
| UnicodeString *firstRow = static_cast<UnicodeString *>(fSafeTable->elementAt(states->first)); |
| for (states->second=states->first+1; states->second<numStates; states->second++) { |
| UnicodeString *duplRow = static_cast<UnicodeString *>(fSafeTable->elementAt(states->second)); |
| bool rowsMatch = true; |
| int32_t numCols = firstRow->length(); |
| for (int32_t col=0; col < numCols; ++col) { |
| int32_t firstVal = firstRow->charAt(col); |
| int32_t duplVal = duplRow->charAt(col); |
| if (!((firstVal == duplVal) || |
| ((firstVal == states->first || firstVal == states->second) && |
| (duplVal == states->first || duplVal == states->second)))) { |
| rowsMatch = false; |
| break; |
| } |
| } |
| if (rowsMatch) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| void RBBITableBuilder::removeState(IntPair duplStates) { |
| const int32_t keepState = duplStates.first; |
| const int32_t duplState = duplStates.second; |
| U_ASSERT(keepState < duplState); |
| U_ASSERT(duplState < fDStates->size()); |
| |
| RBBIStateDescriptor *duplSD = (RBBIStateDescriptor *)fDStates->elementAt(duplState); |
| fDStates->removeElementAt(duplState); |
| delete duplSD; |
| |
| int32_t numStates = fDStates->size(); |
| int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); |
| for (int32_t state=0; state<numStates; ++state) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); |
| for (int32_t col=0; col<numCols; col++) { |
| int32_t existingVal = sd->fDtran->elementAti(col); |
| int32_t newVal = existingVal; |
| if (existingVal == duplState) { |
| newVal = keepState; |
| } else if (existingVal > duplState) { |
| newVal = existingVal - 1; |
| } |
| sd->fDtran->setElementAt(newVal, col); |
| } |
| } |
| } |
| |
| void RBBITableBuilder::removeSafeState(IntPair duplStates) { |
| const int32_t keepState = duplStates.first; |
| const int32_t duplState = duplStates.second; |
| U_ASSERT(keepState < duplState); |
| U_ASSERT(duplState < fSafeTable->size()); |
| |
| fSafeTable->removeElementAt(duplState); // Note that fSafeTable has a deleter function |
| // and will auto-delete the removed element. |
| int32_t numStates = fSafeTable->size(); |
| for (int32_t state=0; state<numStates; ++state) { |
| UnicodeString *sd = (UnicodeString *)fSafeTable->elementAt(state); |
| int32_t numCols = sd->length(); |
| for (int32_t col=0; col<numCols; col++) { |
| int32_t existingVal = sd->charAt(col); |
| int32_t newVal = existingVal; |
| if (existingVal == duplState) { |
| newVal = keepState; |
| } else if (existingVal > duplState) { |
| newVal = existingVal - 1; |
| } |
| sd->setCharAt(col, static_cast<char16_t>(newVal)); |
| } |
| } |
| } |
| |
| |
| /* |
| * RemoveDuplicateStates |
| */ |
| int32_t RBBITableBuilder::removeDuplicateStates() { |
| IntPair dupls = {3, 0}; |
| int32_t numStatesRemoved = 0; |
| |
| while (findDuplicateState(&dupls)) { |
| // printf("Removing duplicate states (%d, %d)\n", dupls.first, dupls.second); |
| removeState(dupls); |
| ++numStatesRemoved; |
| } |
| return numStatesRemoved; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // getTableSize() Calculate the size of the runtime form of this |
| // state transition table. |
| // |
| //----------------------------------------------------------------------------- |
| int32_t RBBITableBuilder::getTableSize() const { |
| int32_t size = 0; |
| int32_t numRows; |
| int32_t numCols; |
| int32_t rowSize; |
| |
| if (fTree == NULL) { |
| return 0; |
| } |
| |
| size = offsetof(RBBIStateTable, fTableData); // The header, with no rows to the table. |
| |
| numRows = fDStates->size(); |
| numCols = fRB->fSetBuilder->getNumCharCategories(); |
| |
| if (use8BitsForTable()) { |
| rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; |
| } else { |
| rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; |
| } |
| size += numRows * rowSize; |
| return size; |
| } |
| |
| bool RBBITableBuilder::use8BitsForTable() const { |
| return fDStates->size() <= kMaxStateFor8BitsTable; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // exportTable() export the state transition table in the format required |
| // by the runtime engine. getTableSize() bytes of memory |
| // must be available at the output address "where". |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::exportTable(void *where) { |
| RBBIStateTable *table = (RBBIStateTable *)where; |
| uint32_t state; |
| int col; |
| |
| if (U_FAILURE(*fStatus) || fTree == NULL) { |
| return; |
| } |
| |
| int32_t catCount = fRB->fSetBuilder->getNumCharCategories(); |
| if (catCount > 0x7fff || |
| fDStates->size() > 0x7fff) { |
| *fStatus = U_BRK_INTERNAL_ERROR; |
| return; |
| } |
| |
| table->fNumStates = fDStates->size(); |
| table->fDictCategoriesStart = fRB->fSetBuilder->getDictCategoriesStart(); |
| table->fLookAheadResultsSize = fLASlotsInUse == ACCEPTING_UNCONDITIONAL ? 0 : fLASlotsInUse + 1; |
| table->fFlags = 0; |
| if (use8BitsForTable()) { |
| table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; |
| table->fFlags |= RBBI_8BITS_ROWS; |
| } else { |
| table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; |
| } |
| if (fRB->fLookAheadHardBreak) { |
| table->fFlags |= RBBI_LOOKAHEAD_HARD_BREAK; |
| } |
| if (fRB->fSetBuilder->sawBOF()) { |
| table->fFlags |= RBBI_BOF_REQUIRED; |
| } |
| |
| for (state=0; state<table->fNumStates; state++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); |
| RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); |
| if (use8BitsForTable()) { |
| U_ASSERT (sd->fAccepting <= 255); |
| U_ASSERT (sd->fLookAhead <= 255); |
| U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 255); |
| row->r8.fAccepting = sd->fAccepting; |
| row->r8.fLookAhead = sd->fLookAhead; |
| row->r8.fTagsIdx = sd->fTagsIdx; |
| for (col=0; col<catCount; col++) { |
| U_ASSERT (sd->fDtran->elementAti(col) <= kMaxStateFor8BitsTable); |
| row->r8.fNextState[col] = sd->fDtran->elementAti(col); |
| } |
| } else { |
| U_ASSERT (sd->fAccepting <= 0xffff); |
| U_ASSERT (sd->fLookAhead <= 0xffff); |
| U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 0xffff); |
| row->r16.fAccepting = sd->fAccepting; |
| row->r16.fLookAhead = sd->fLookAhead; |
| row->r16.fTagsIdx = sd->fTagsIdx; |
| for (col=0; col<catCount; col++) { |
| row->r16.fNextState[col] = sd->fDtran->elementAti(col); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Synthesize a safe state table from the main state table. |
| */ |
| void RBBITableBuilder::buildSafeReverseTable(UErrorCode &status) { |
| // The safe table creation has three steps: |
| |
| // 1. Identifiy pairs of character classes that are "safe." Safe means that boundaries |
| // following the pair do not depend on context or state before the pair. To test |
| // whether a pair is safe, run it through the main forward state table, starting |
| // from each state. If the the final state is the same, no matter what the starting state, |
| // the pair is safe. |
| // |
| // 2. Build a state table that recognizes the safe pairs. It's similar to their |
| // forward table, with a column for each input character [class], and a row for |
| // each state. Row 1 is the start state, and row 0 is the stop state. Initially |
| // create an additional state for each input character category; being in |
| // one of these states means that the character has been seen, and is potentially |
| // the first of a pair. In each of these rows, the entry for the second character |
| // of a safe pair is set to the stop state (0), indicating that a match was found. |
| // All other table entries are set to the state corresponding the current input |
| // character, allowing that charcter to be the of a start following pair. |
| // |
| // Because the safe rules are to be run in reverse, moving backwards in the text, |
| // the first and second pair categories are swapped when building the table. |
| // |
| // 3. Compress the table. There are typically many rows (states) that are |
| // equivalent - that have zeroes (match completed) in the same columns - |
| // and can be folded together. |
| |
| // Each safe pair is stored as two UChars in the safePair string. |
| UnicodeString safePairs; |
| |
| int32_t numCharClasses = fRB->fSetBuilder->getNumCharCategories(); |
| int32_t numStates = fDStates->size(); |
| |
| for (int32_t c1=0; c1<numCharClasses; ++c1) { |
| for (int32_t c2=0; c2 < numCharClasses; ++c2) { |
| int32_t wantedEndState = -1; |
| int32_t endState = 0; |
| for (int32_t startState = 1; startState < numStates; ++startState) { |
| RBBIStateDescriptor *startStateD = static_cast<RBBIStateDescriptor *>(fDStates->elementAt(startState)); |
| int32_t s2 = startStateD->fDtran->elementAti(c1); |
| RBBIStateDescriptor *s2StateD = static_cast<RBBIStateDescriptor *>(fDStates->elementAt(s2)); |
| endState = s2StateD->fDtran->elementAti(c2); |
| if (wantedEndState < 0) { |
| wantedEndState = endState; |
| } else { |
| if (wantedEndState != endState) { |
| break; |
| } |
| } |
| } |
| if (wantedEndState == endState) { |
| safePairs.append((char16_t)c1); |
| safePairs.append((char16_t)c2); |
| // printf("(%d, %d) ", c1, c2); |
| } |
| } |
| // printf("\n"); |
| } |
| |
| // Populate the initial safe table. |
| // The table as a whole is UVector<UnicodeString> |
| // Each row is represented by a UnicodeString, being used as a Vector<int16>. |
| // Row 0 is the stop state. |
| // Row 1 is the start sate. |
| // Row 2 and beyond are other states, initially one per char class, but |
| // after initial construction, many of the states will be combined, compacting the table. |
| // The String holds the nextState data only. The four leading fields of a row, fAccepting, |
| // fLookAhead, etc. are not needed for the safe table, and are omitted at this stage of building. |
| |
| U_ASSERT(fSafeTable == nullptr); |
| fSafeTable = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, numCharClasses + 2, status); |
| for (int32_t row=0; row<numCharClasses + 2; ++row) { |
| fSafeTable->addElement(new UnicodeString(numCharClasses, 0, numCharClasses+4), status); |
| } |
| |
| // From the start state, each input char class transitions to the state for that input. |
| UnicodeString &startState = *static_cast<UnicodeString *>(fSafeTable->elementAt(1)); |
| for (int32_t charClass=0; charClass < numCharClasses; ++charClass) { |
| // Note: +2 for the start & stop state. |
| startState.setCharAt(charClass, static_cast<char16_t>(charClass+2)); |
| } |
| |
| // Initially make every other state table row look like the start state row, |
| for (int32_t row=2; row<numCharClasses+2; ++row) { |
| UnicodeString &rowState = *static_cast<UnicodeString *>(fSafeTable->elementAt(row)); |
| rowState = startState; // UnicodeString assignment, copies contents. |
| } |
| |
| // Run through the safe pairs, set the next state to zero when pair has been seen. |
| // Zero being the stop state, meaning we found a safe point. |
| for (int32_t pairIdx=0; pairIdx<safePairs.length(); pairIdx+=2) { |
| int32_t c1 = safePairs.charAt(pairIdx); |
| int32_t c2 = safePairs.charAt(pairIdx + 1); |
| |
| UnicodeString &rowState = *static_cast<UnicodeString *>(fSafeTable->elementAt(c2 + 2)); |
| rowState.setCharAt(c1, 0); |
| } |
| |
| // Remove duplicate or redundant rows from the table. |
| IntPair states = {1, 0}; |
| while (findDuplicateSafeState(&states)) { |
| // printf("Removing duplicate safe states (%d, %d)\n", states.first, states.second); |
| removeSafeState(states); |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // getSafeTableSize() Calculate the size of the runtime form of this |
| // safe state table. |
| // |
| //----------------------------------------------------------------------------- |
| int32_t RBBITableBuilder::getSafeTableSize() const { |
| int32_t size = 0; |
| int32_t numRows; |
| int32_t numCols; |
| int32_t rowSize; |
| |
| if (fSafeTable == nullptr) { |
| return 0; |
| } |
| |
| size = offsetof(RBBIStateTable, fTableData); // The header, with no rows to the table. |
| |
| numRows = fSafeTable->size(); |
| numCols = fRB->fSetBuilder->getNumCharCategories(); |
| |
| if (use8BitsForSafeTable()) { |
| rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; |
| } else { |
| rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; |
| } |
| size += numRows * rowSize; |
| return size; |
| } |
| |
| bool RBBITableBuilder::use8BitsForSafeTable() const { |
| return fSafeTable->size() <= kMaxStateFor8BitsTable; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // exportSafeTable() export the state transition table in the format required |
| // by the runtime engine. getTableSize() bytes of memory |
| // must be available at the output address "where". |
| // |
| //----------------------------------------------------------------------------- |
| void RBBITableBuilder::exportSafeTable(void *where) { |
| RBBIStateTable *table = (RBBIStateTable *)where; |
| uint32_t state; |
| int col; |
| |
| if (U_FAILURE(*fStatus) || fSafeTable == nullptr) { |
| return; |
| } |
| |
| int32_t catCount = fRB->fSetBuilder->getNumCharCategories(); |
| if (catCount > 0x7fff || |
| fSafeTable->size() > 0x7fff) { |
| *fStatus = U_BRK_INTERNAL_ERROR; |
| return; |
| } |
| |
| table->fNumStates = fSafeTable->size(); |
| table->fFlags = 0; |
| if (use8BitsForSafeTable()) { |
| table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; |
| table->fFlags |= RBBI_8BITS_ROWS; |
| } else { |
| table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; |
| } |
| |
| for (state=0; state<table->fNumStates; state++) { |
| UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(state); |
| RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); |
| if (use8BitsForSafeTable()) { |
| row->r8.fAccepting = 0; |
| row->r8.fLookAhead = 0; |
| row->r8.fTagsIdx = 0; |
| for (col=0; col<catCount; col++) { |
| U_ASSERT(rowString->charAt(col) <= kMaxStateFor8BitsTable); |
| row->r8.fNextState[col] = static_cast<uint8_t>(rowString->charAt(col)); |
| } |
| } else { |
| row->r16.fAccepting = 0; |
| row->r16.fLookAhead = 0; |
| row->r16.fTagsIdx = 0; |
| for (col=0; col<catCount; col++) { |
| row->r16.fNextState[col] = rowString->charAt(col); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // printSet Debug function. Print the contents of a UVector |
| // |
| //----------------------------------------------------------------------------- |
| #ifdef RBBI_DEBUG |
| void RBBITableBuilder::printSet(UVector *s) { |
| int32_t i; |
| for (i=0; i<s->size(); i++) { |
| const RBBINode *v = static_cast<const RBBINode *>(s->elementAt(i)); |
| RBBIDebugPrintf("%5d", v==NULL? -1 : v->fSerialNum); |
| } |
| RBBIDebugPrintf("\n"); |
| } |
| #endif |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // printStates Debug Function. Dump the fully constructed state transition table. |
| // |
| //----------------------------------------------------------------------------- |
| #ifdef RBBI_DEBUG |
| void RBBITableBuilder::printStates() { |
| int c; // input "character" |
| int n; // state number |
| |
| RBBIDebugPrintf("state | i n p u t s y m b o l s \n"); |
| RBBIDebugPrintf(" | Acc LA Tag"); |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf(" %3d", c); |
| } |
| RBBIDebugPrintf("\n"); |
| RBBIDebugPrintf(" |---------------"); |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf("----"); |
| } |
| RBBIDebugPrintf("\n"); |
| |
| for (n=0; n<fDStates->size(); n++) { |
| RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); |
| RBBIDebugPrintf(" %3d | " , n); |
| RBBIDebugPrintf("%3d %3d %5d ", sd->fAccepting, sd->fLookAhead, sd->fTagsIdx); |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf(" %3d", sd->fDtran->elementAti(c)); |
| } |
| RBBIDebugPrintf("\n"); |
| } |
| RBBIDebugPrintf("\n\n"); |
| } |
| #endif |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // printSafeTable Debug Function. Dump the fully constructed safe table. |
| // |
| //----------------------------------------------------------------------------- |
| #ifdef RBBI_DEBUG |
| void RBBITableBuilder::printReverseTable() { |
| int c; // input "character" |
| int n; // state number |
| |
| RBBIDebugPrintf(" Safe Reverse Table \n"); |
| if (fSafeTable == nullptr) { |
| RBBIDebugPrintf(" --- nullptr ---\n"); |
| return; |
| } |
| RBBIDebugPrintf("state | i n p u t s y m b o l s \n"); |
| RBBIDebugPrintf(" | Acc LA Tag"); |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf(" %2d", c); |
| } |
| RBBIDebugPrintf("\n"); |
| RBBIDebugPrintf(" |---------------"); |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf("---"); |
| } |
| RBBIDebugPrintf("\n"); |
| |
| for (n=0; n<fSafeTable->size(); n++) { |
| UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(n); |
| RBBIDebugPrintf(" %3d | " , n); |
| RBBIDebugPrintf("%3d %3d %5d ", 0, 0, 0); // Accepting, LookAhead, Tags |
| for (c=0; c<fRB->fSetBuilder->getNumCharCategories(); c++) { |
| RBBIDebugPrintf(" %2d", rowString->charAt(c)); |
| } |
| RBBIDebugPrintf("\n"); |
| } |
| RBBIDebugPrintf("\n\n"); |
| } |
| #endif |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // printRuleStatusTable Debug Function. Dump the common rule status table |
| // |
| //----------------------------------------------------------------------------- |
| #ifdef RBBI_DEBUG |
| void RBBITableBuilder::printRuleStatusTable() { |
| int32_t thisRecord = 0; |
| int32_t nextRecord = 0; |
| int i; |
| UVector *tbl = fRB->fRuleStatusVals; |
| |
| RBBIDebugPrintf("index | tags \n"); |
| RBBIDebugPrintf("-------------------\n"); |
| |
| while (nextRecord < tbl->size()) { |
| thisRecord = nextRecord; |
| nextRecord = thisRecord + tbl->elementAti(thisRecord) + 1; |
| RBBIDebugPrintf("%4d ", thisRecord); |
| for (i=thisRecord+1; i<nextRecord; i++) { |
| RBBIDebugPrintf(" %5d", tbl->elementAti(i)); |
| } |
| RBBIDebugPrintf("\n"); |
| } |
| RBBIDebugPrintf("\n\n"); |
| } |
| #endif |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // RBBIStateDescriptor Methods. This is a very struct-like class |
| // Most access is directly to the fields. |
| // |
| //----------------------------------------------------------------------------- |
| |
| RBBIStateDescriptor::RBBIStateDescriptor(int lastInputSymbol, UErrorCode *fStatus) { |
| fMarked = FALSE; |
| fAccepting = 0; |
| fLookAhead = 0; |
| fTagsIdx = 0; |
| fTagVals = NULL; |
| fPositions = NULL; |
| fDtran = NULL; |
| |
| fDtran = new UVector32(lastInputSymbol+1, *fStatus); |
| if (U_FAILURE(*fStatus)) { |
| return; |
| } |
| if (fDtran == NULL) { |
| *fStatus = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| fDtran->setSize(lastInputSymbol+1); // fDtran needs to be pre-sized. |
| // It is indexed by input symbols, and will |
| // hold the next state number for each |
| // symbol. |
| } |
| |
| |
| RBBIStateDescriptor::~RBBIStateDescriptor() { |
| delete fPositions; |
| delete fDtran; |
| delete fTagVals; |
| fPositions = NULL; |
| fDtran = NULL; |
| fTagVals = NULL; |
| } |
| |
| U_NAMESPACE_END |
| |
| #endif /* #if !UCONFIG_NO_BREAK_ITERATION */ |