//---------------------------------------------------------------------------------------- // // doParseAction Do some action during rule parsing. // Called by the parse state machine. // Actions build the parse tree and Unicode Sets, // and maintain the parse stack for nested expressions. // // TODO: unify EParseAction and RBBI_RuleParseAction enum types. // They represent exactly the same thing. They're separate // only to work around enum forward declaration restrictions // in some compilers, while at the same time avoiding multiple // definitions problems. I'm sure that there's a better way. // //---------------------------------------------------------------------------------------- UBool RBBIRuleScanner::doParseActions(EParseAction action) { RBBINode *n = NULL; UBool returnVal = TRUE; switch ((RBBI_RuleParseAction)action) { case doExprStart: pushNewNode(RBBINode::opStart); fRuleNum++; break; case doExprOrOperator: { fixOpStack(RBBINode::precOpCat); RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; RBBINode *orNode = pushNewNode(RBBINode::opOr); orNode->fLeftChild = operandNode; operandNode->fParent = orNode; } break; case doExprCatOperator: // concatenation operator. // For the implicit concatenation of adjacent terms in an expression that are // not separated by any other operator. Action is invoked between the // actions for the two terms. { fixOpStack(RBBINode::precOpCat); RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; RBBINode *catNode = pushNewNode(RBBINode::opCat); catNode->fLeftChild = operandNode; operandNode->fParent = catNode; } break; case doLParen: // Open Paren. // The openParen node is a dummy operation type with a low precedence, // which has the affect of ensuring that any real binary op that // follows within the parens binds more tightly to the operands than // stuff outside of the parens. pushNewNode(RBBINode::opLParen); break; case doExprRParen: fixOpStack(RBBINode::precLParen); break; case doNOP: break; case doStartAssign: // We've just scanned "$variable = " // The top of the node stack has the $variable ref node. // Save the start position of the RHS text in the StartExpression node // that precedes the $variableReference node on the stack. // This will eventually be used when saving the full $variable replacement // text as a string. n = fNodeStack[fNodeStackPtr-1]; n->fFirstPos = fNextIndex; // move past the '=' // Push a new start-of-expression node; needed to keep parse of the // RHS expression happy. pushNewNode(RBBINode::opStart); break; case doEndAssign: { // We have reached the end of an assignement statement. // Current scan char is the ';' that terminates the assignment. // Terminate expression, leaves expression parse tree rooted in TOS node. fixOpStack(RBBINode::precStart); RBBINode *startExprNode = fNodeStack[fNodeStackPtr-2]; RBBINode *varRefNode = fNodeStack[fNodeStackPtr-1]; RBBINode *RHSExprNode = fNodeStack[fNodeStackPtr]; // Save original text of right side of assignment, excluding the terminating ';' // in the root of the node for the right-hand-side expression. RHSExprNode->fFirstPos = startExprNode->fFirstPos; RHSExprNode->fLastPos = fScanIndex; fRB->fRules.extractBetween(RHSExprNode->fFirstPos, RHSExprNode->fLastPos, RHSExprNode->fText); // Expression parse tree becomes l. child of the $variable reference node. varRefNode->fLeftChild = RHSExprNode; RHSExprNode->fParent = varRefNode; // Make a symbol table entry for the $variableRef node. fSymbolTable->addEntry(varRefNode->fText, varRefNode, *fRB->fStatus); if (U_FAILURE(*fRB->fStatus)) { // This is a round-about way to get the parse position set // so that duplicate symbols error messages include a line number. UErrorCode t = *fRB->fStatus; *fRB->fStatus = U_ZERO_ERROR; error(t); } // Clean up the stack. delete startExprNode; fNodeStackPtr-=3; break; } case doEndOfRule: { fixOpStack(RBBINode::precStart); // Terminate expression, leaves expression if (U_FAILURE(*fRB->fStatus)) { // parse tree rooted in TOS node. break; } #ifdef RBBI_DEBUG if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "rtree")) { printNodeStack("end of rule"); } #endif U_ASSERT(fNodeStackPtr == 1); // If this rule includes a look-ahead '/', add a endMark node to the // expression tree. if (fLookAheadRule) { RBBINode *thisRule = fNodeStack[fNodeStackPtr]; RBBINode *endNode = pushNewNode(RBBINode::endMark); RBBINode *catNode = pushNewNode(RBBINode::opCat); fNodeStackPtr -= 2; catNode->fLeftChild = thisRule; catNode->fRightChild = endNode; fNodeStack[fNodeStackPtr] = catNode; endNode->fVal = fRuleNum; endNode->fLookAheadEnd = TRUE; } // All rule expressions are ORed together. // The ';' that terminates an expression really just functions as a '|' with // a low operator prededence. // // Each of the four sets of rules are collected separately. // (forward, reverse, safe_forward, safe_reverse) // OR this rule into the appropriate group of them. // RBBINode **destRules = (fReverseRule? &fRB->fReverseTree : fRB->fDefaultTree); if (*destRules != NULL) { // This is not the first rule encounted. // OR previous stuff (from *destRules) // with the current rule expression (on the Node Stack) // with the resulting OR expression going to *destRules // RBBINode *thisRule = fNodeStack[fNodeStackPtr]; RBBINode *prevRules = *destRules; RBBINode *orNode = pushNewNode(RBBINode::opOr); orNode->fLeftChild = prevRules; prevRules->fParent = orNode; orNode->fRightChild = thisRule; thisRule->fParent = orNode; *destRules = orNode; } else { // This is the first rule encountered (for this direction). // Just move its parse tree from the stack to *destRules. *destRules = fNodeStack[fNodeStackPtr]; } fReverseRule = FALSE; // in preparation for the next rule. fLookAheadRule = FALSE; fNodeStackPtr = 0; } break; case doRuleError: error(U_BRK_RULE_SYNTAX); returnVal = FALSE; break; case doVariableNameExpectedErr: error(U_BRK_RULE_SYNTAX); break; // // Unary operands + ? * // These all appear after the operand to which they apply. // When we hit one, the operand (may be a whole sub expression) // will be on the top of the stack. // Unary Operator becomes TOS, with the old TOS as its one child. case doUnaryOpPlus: { RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; RBBINode *plusNode = pushNewNode(RBBINode::opPlus); plusNode->fLeftChild = operandNode; operandNode->fParent = plusNode; } break; case doUnaryOpQuestion: { RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; RBBINode *qNode = pushNewNode(RBBINode::opQuestion); qNode->fLeftChild = operandNode; operandNode->fParent = qNode; } break; case doUnaryOpStar: { RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; RBBINode *starNode = pushNewNode(RBBINode::opStar); starNode->fLeftChild = operandNode; operandNode->fParent = starNode; } break; case doRuleChar: // A "Rule Character" is any single character that is a literal part // of the regular expression. Like a, b and c in the expression "(abc*) | [:L:]" // These are pretty uncommon in break rules; the terms are more commonly // sets. To keep things uniform, treat these characters like as // sets that just happen to contain only one character. { n = pushNewNode(RBBINode::setRef); findSetFor(fC.fChar, n); n->fFirstPos = fScanIndex; n->fLastPos = fNextIndex; fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); break; } case doDotAny: // scanned a ".", meaning match any single character. { n = pushNewNode(RBBINode::setRef); findSetFor(kAny, n); n->fFirstPos = fScanIndex; n->fLastPos = fNextIndex; fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); break; } case doSlash: // Scanned a '/', which identifies a look-ahead break position in a rule. n = pushNewNode(RBBINode::lookAhead); n->fVal = fRuleNum; n->fFirstPos = fScanIndex; n->fLastPos = fNextIndex; fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); fLookAheadRule = TRUE; break; case doStartTagValue: // Scanned a '{', the opening delimiter for a tag value within a rule. n = pushNewNode(RBBINode::tag); n->fVal = 0; n->fFirstPos = fScanIndex; n->fLastPos = fNextIndex; break; case doTagDigit: // Just scanned a decimal digit that's part of a tag value { n = fNodeStack[fNodeStackPtr]; uint32_t v = u_charDigitValue(fC.fChar); U_ASSERT(v < 10); n->fVal = n->fVal*10 + v; break; } case doTagValue: n = fNodeStack[fNodeStackPtr]; n->fLastPos = fNextIndex; fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); break; case doTagExpectedError: error(U_BRK_MALFORMED_RULE_TAG); returnVal = FALSE; break; case doOptionStart: // Scanning a !!option. At the start of string. fOptionStart = fScanIndex; break; case doOptionEnd: { UnicodeString opt(fRB->fRules, fOptionStart, fScanIndex-fOptionStart); if (opt == UNICODE_STRING("chain", 5)) { fRB->fChainRules = TRUE; } else if (opt == UNICODE_STRING("LBCMNoChain", 11)) { fRB->fLBCMNoChain = TRUE; } else if (opt == UNICODE_STRING("forward", 7)) { fRB->fDefaultTree = &fRB->fForwardTree; } else if (opt == UNICODE_STRING("reverse", 7)) { fRB->fDefaultTree = &fRB->fReverseTree; } else if (opt == UNICODE_STRING("safe_forward", 12)) { fRB->fDefaultTree = &fRB->fSafeFwdTree; } else if (opt == UNICODE_STRING("safe_reverse", 12)) { fRB->fDefaultTree = &fRB->fSafeRevTree; } else if (opt == UNICODE_STRING("lookAheadHardBreak", 18)) { fRB->fLookAheadHardBreak = TRUE; } else { error(U_BRK_UNRECOGNIZED_OPTION); } } break; case doReverseDir: fReverseRule = TRUE; break; case doStartVariableName: n = pushNewNode(RBBINode::varRef); if (U_FAILURE(*fRB->fStatus)) { break; } n->fFirstPos = fScanIndex; break; case doEndVariableName: n = fNodeStack[fNodeStackPtr]; if (n==NULL || n->fType != RBBINode::varRef) { error(U_BRK_INTERNAL_ERROR); break; } n->fLastPos = fScanIndex; fRB->fRules.extractBetween(n->fFirstPos+1, n->fLastPos, n->fText); // Look the newly scanned name up in the symbol table // If there's an entry, set the l. child of the var ref to the replacement expression. // (We also pass through here when scanning assignments, but no harm is done, other // than a slight wasted effort that seems hard to avoid. Lookup will be null) n->fLeftChild = fSymbolTable->lookupNode(n->fText); break; case doCheckVarDef: n = fNodeStack[fNodeStackPtr]; if (n->fLeftChild == NULL) { error(U_BRK_UNDEFINED_VARIABLE); returnVal = FALSE; } break; case doExprFinished: break; case doRuleErrorAssignExpr: error(U_BRK_ASSIGN_ERROR); returnVal = FALSE; break; case doExit: returnVal = FALSE; break; case doScanUnicodeSet: scanSet(); break; default: error(U_BRK_INTERNAL_ERROR); returnVal = FALSE; break; } return returnVal; }
//--------------------------------------------------------------------------- // readObjAscii helper function. // This function reads face information from the file starting // at the location of fptr, and stores those in // <tris>. The function currently either returns TRUE // or throws an error. // //--------------------------------------------------------------------------- bool readObjFaces( FileHandle &fptr, VertContainer* verts, TriContainer* tris ) { vector<hduVector3Df> vertTextures; vector<Triangle> triTextures; long vertex_indices[3] = {0,0,0}; const int STRING_LENGTH = 1024; char line[STRING_LENGTH]; while (getNextLine(fptr,line)) { char identifier[STRING_LENGTH]; if (sscanf(line,"%s",identifier) != 1) { continue; } // Keep track of which vertex index we are at currently while reading // for vertex positions, texture coordinates, and normals. This is // necessary because geometries can reference these // in relation to where // they are located themselves (e.g. "-5" as a reference // means "five vertices // above where this statement is."). const int V_INDEX = 0; const int VT_INDEX = 1; const int VN_INDEX = 2; if (stringis_noCase(identifier,"v")) { vertex_indices[V_INDEX]++; } if (stringis_noCase(identifier,"vt")) { vertex_indices[VT_INDEX]++; } if (stringis_noCase(identifier,"vn")) { vertex_indices[VN_INDEX]++; } // Parse faces and add resulting triangles. if (stringis_noCase(identifier,"f") || stringis_noCase(identifier,"fo")) { char *line_pointer = line+2; // Start parsing after the "f " or "fo" vector<int>v_vertexList; vector<int>v_vertexTextureList; // Parse a single line of vertex information, resulting in // a list containing the vertex indices for one face. while (line_pointer) { // Parse the vertex set, advance the line_pointer // to the next vertex set. // Vertex sets can appear as // <x>, <x>/<y>, <x>//<y>, or <x>/<y>/<z>. int pt[3]; bool pt_b[3]; line_pointer = scanSet(line_pointer,pt,pt_b); // Check for validity. // Vertex indices must be non-zero. // If vertex indices are negative, they are converted // to absolute indices. for (int i=0; i < 3; i++) { if (!pt_b[i]) { continue; } if (pt[i] < 0) { pt[i] = vertex_indices[i] + pt[i] + 1; } if (pt[i] <= 0 ) { assert(false); throw "readObjFaces invalid vertex (zero or negative) read."; } // Subtract one because SurfaceModel indices are // zero-ordered whereas OBJ indices are 1-ordered. // E.G. a "1" as a vertex index in the OBJ file // refers to the first vertex in the file, which // is array element 0 in the verts(). pt[i]--; } // Push back the vertex positional index. // Note that we ignore the normal and texture information, // but would add those into their respective lists as well // if we needed that information. if (pt_b[V_INDEX] == TRUE) { if (pt[V_INDEX] >= verts->size()) { assert(false); throw "readObjFace face specifies invalid vertex."; } v_vertexList.push_back(pt[V_INDEX]); // Push back any corresponding texture for this vertex. // If none, just stuff a zero. if (pt_b[VT_INDEX] == TRUE) { if (pt[VT_INDEX] >= vertTextures.size()) throw "readObjFace face specifies invalid vertex texture."; v_vertexTextureList.push_back(pt[VT_INDEX]); } else { v_vertexTextureList.push_back(0); } } } if (v_vertexList.size() < 3) throw "readObjFace face list with fewer than three vertices read."; if (v_vertexList.size() == 3) { // Push back indices of vertices, e.g. (0,1,2), (2,3,0) tris->push_back(Triangle(v_vertexList[0], v_vertexList[1], v_vertexList[2])); triTextures.push_back(Triangle(v_vertexTextureList[0], v_vertexTextureList[1], v_vertexTextureList[2])); } else if (v_vertexList.size() == 4) { tris->push_back(Triangle(v_vertexList[0], v_vertexList[1], v_vertexList[2])); triTextures.push_back(Triangle(v_vertexTextureList[0], v_vertexTextureList[1], v_vertexTextureList[2])); tris->push_back(Triangle(v_vertexList[0], v_vertexList[2], v_vertexList[3])); triTextures.push_back(Triangle(v_vertexTextureList[0], v_vertexTextureList[2], v_vertexTextureList[3])); } else { throw "face found with more than four vertices."; } } // Ignore other types of geometric data, such as surfaces, // because we currently do not use those. In the future, // if we were to parse surfaces, curves, groups, render/display // attributes, or other data, we would add that information here. if (stringis_noCase(identifier,"cstype")) { ; } if (stringis_noCase(identifier,"surf")) { ; } if (stringis_noCase(identifier,"g")) { ; } } return TRUE; }