/*--------------------------------*- C++ -*----------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2016-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . Description Flex-based parsing of STL ASCII format \*---------------------------------------------------------------------------*/ %option prefix="yySTL" %option yyclass="yySTLFlexLexer" %{ /* ------------------------------------------------------------------------ *\ ------ local definitions \* ------------------------------------------------------------------------ */ #include "STLAsciiParse.H" #include "STLReader.H" #include "OSspecific.H" // Flex may use register, which is deprecated and incompatible with C++17 #pragma clang diagnostic ignored "-Wdeprecated-register" // A 'nothing' define to effectively remove from code as well (issue #3337) #undef register #define register using namespace Foam; // Dummy yyFlexLexer::yylex() to keep the linker happy. It is not called //! \cond dummy #if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34 int yyFlexLexer::yylex() { FatalErrorInFunction << "Should not have called this function" << abort(FatalError); return 0; } #endif //! \endcond // Dummy yywrap to keep yylex happy at compile time. // It is called by yylex but is not used as the mechanism to change file. // See <> //! \cond dummy #if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34 extern "C" int yywrap() #else int yySTLFlexLexer::yywrap() #endif { return 1; } //! \endcond //- A lexer for parsing STL ASCII files. // Returns DynamicList(s) of points and facets (zoneIds). // The facets are within a solid/endsolid grouping class STLAsciiParseFlex : public Detail::STLAsciiParse, public yySTLFlexLexer { word startError_; public: // Constructors //- From input stream, with the estimated number of triangles in the STL STLAsciiParseFlex(istream* is, const label nTrisEstimated) : Detail::STLAsciiParse(nTrisEstimated), yySTLFlexLexer(is) {} // Member Functions //- The lexer function itself int lex(); //- Execute lexer void execute() { while (lex()) {} } }; /* ------------------------------------------------------------------------ *\ ------ cppLexer::yylex() \* ------------------------------------------------------------------------ */ #undef YY_DECL #define YY_DECL int STLAsciiParseFlex::lex() %} white [ \t\f\r] space {white}* some_space {white}+ alpha [_A-Za-z] digit [0-9] intNum [-+]?{digit}+ word ([[:alnum:]]|[[:punct:]])* string {word}({some_space}{word})* expon [Ee][-+]?{digit}+ fract [-+]?(({digit}*"."{digit}+)|({digit}+"."?)) floatNum (({fract}{expon}?)|({digit}+{expon})) x {floatNum} y {floatNum} z {floatNum} solid {space}("solid"|"SOLID"){space} color {space}("color"|"COLOR"){some_space}{floatNum}{some_space}{floatNum}{some_space}{floatNum}{space} facet {space}("facet"|"FACET"){space} normal {space}("normal"|"NORMAL"){space} point {space}{x}{some_space}{y}{some_space}{z}{space} outerloop {space}("outer"{some_space}"loop")|("OUTER"{some_space}"LOOP"){space} vertex {space}("vertex"|"VERTEX"){space} endloop {space}("endloop"|"ENDLOOP"){space} endfacet {space}("endfacet"|"ENDFACET"){space} endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})* /* ------------------------------------------------------------------------ *\ ----- Exclusive start states ----- \* ------------------------------------------------------------------------ */ %option stack %x readSolidName %x readFacet %x readNormal %x readVertices %x readVertex %x stlError %% %{ static const char* stateNames[7] = { "reading solid", "reading solid name", "reading facet", "reading normal", "reading vertices", "reading vertex", "error" }; static const char* stateExpects[7] = { "'solid', 'color', 'facet' or 'endsolid'", "", "'normal', 'outer loop' or 'endfacet'", " ", "'vertex' or 'endloop'", " ", "" }; %} /* ------------------------------------------------------------------------ *\ ------ Start Lexing ------ \* ------------------------------------------------------------------------ */ /* ------ Reading control header ------ */ {solid} { BEGIN(readSolidName); } {string} { beginSolid(word::validate(YYText())); BEGIN(INITIAL); } {space}\n { beginSolid("solid"); // Could also use solid0, solid1, ... ++lineNum_; BEGIN(INITIAL); } {color} { /* ignore 'color' */ } {facet} { beginFacet(); BEGIN(readFacet); } {normal} { BEGIN(readNormal); } {point} { /* skip reading normals: normal.x() = strtof(YYText(), &endPtr); normal.y() = strtof(endPtr, &endPtr); normal.z() = strtof(endPtr, &endPtr); normals_.push_back(normal); */ BEGIN(readFacet); } {outerloop} { BEGIN(readVertices); } {vertex} { BEGIN(readVertex); } {space}{intNum}{space} { if (addVertexComponent(float(::atol(YYText())))) { BEGIN(readVertices); } } {space}{floatNum}{space} { if (addVertexComponent(::atof(YYText()))) { BEGIN(readVertices); } } {endloop} { BEGIN(readFacet); } {endfacet} { endFacet(); BEGIN(INITIAL); } {endsolid} { } /* ---------------- Ignore remaining spaces and newlines ------------------ */ <*>{space} {} <*>\n { ++lineNum_; } /* ------------------- Any other characters are errors -------------------- */ <*>. { startError_ = YYText(); yy_push_state(stlError); } /* ---------------------------- Error handler ----------------------------- */ .* { yy_pop_state(); FatalErrorInFunction << "while " << stateNames[YY_START] << " on line " << lineNum_ << nl << " expected " << stateExpects[YY_START] << " but found '" << startError_.c_str() << YYText() << "'" << exit(FatalError); } /* ------------------------ On EOF terminate ---------------------------- */ <> { yyterminate(); } %% // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // bool Foam::fileFormats::STLReader::readAsciiFlex ( const fileName& filename ) { IFstream is(filename); if (!is) { FatalErrorInFunction << "file " << filename << " not found" << exit(FatalError); } // Create with estimated number of triangles in the STL. // 180 bytes / triangle. For simplicity, ignore compression const auto fileLen = is.fileSize(); const label nTrisEstimated = ( (fileLen > 0) ? max(label(100), label(fileLen/180)) : label(100) ); STLAsciiParseFlex lexer(&(is.stdStream()), nTrisEstimated); lexer.execute(); transfer(lexer); return true; } // ************************************************************************* //