/*--------------------------------*- 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;
}
// ************************************************************************* //