2020-08-11 09:10:23 +00:00
|
|
|
// © 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
|
|
|
|
|
|
|
|
#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);
|
2020-12-18 21:02:48 +00:00
|
|
|
RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row;
|
|
|
|
r8->fAccepting = sd->fAccepting;
|
|
|
|
r8->fLookAhead = sd->fLookAhead;
|
|
|
|
r8->fTagsIdx = sd->fTagsIdx;
|
2020-08-11 09:10:23 +00:00
|
|
|
for (col=0; col<catCount; col++) {
|
|
|
|
U_ASSERT (sd->fDtran->elementAti(col) <= kMaxStateFor8BitsTable);
|
2020-12-18 21:02:48 +00:00
|
|
|
r8->fNextState[col] = sd->fDtran->elementAti(col);
|
2020-08-11 09:10:23 +00:00
|
|
|
}
|
|
|
|
} 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()) {
|
2020-12-18 21:02:48 +00:00
|
|
|
RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row;
|
|
|
|
r8->fAccepting = 0;
|
|
|
|
r8->fLookAhead = 0;
|
|
|
|
r8->fTagsIdx = 0;
|
2020-08-11 09:10:23 +00:00
|
|
|
for (col=0; col<catCount; col++) {
|
|
|
|
U_ASSERT(rowString->charAt(col) <= kMaxStateFor8BitsTable);
|
2020-12-18 21:02:48 +00:00
|
|
|
r8->fNextState[col] = static_cast<uint8_t>(rowString->charAt(col));
|
2020-08-11 09:10:23 +00:00
|
|
|
}
|
|
|
|
} 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 */
|