mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Rework variable length token storage to use string pool; eliminate need for rereads
This commit is contained in:
@@ -91,7 +91,7 @@ namespace {
|
||||
|
||||
unsigned int parse_state = 0;
|
||||
builder_.clear();
|
||||
builder_.push_back('\'');
|
||||
// builder_.push_back('\'');
|
||||
char current_char;
|
||||
int codepage = 1;
|
||||
unsigned int hex = 0;
|
||||
@@ -169,7 +169,7 @@ namespace {
|
||||
}
|
||||
stream_.increment();
|
||||
}
|
||||
builder_.push_back('\'');
|
||||
// builder_.push_back('\'');
|
||||
|
||||
if (mode == IfcParse::IfcCharacterDecoder::UTF8) {
|
||||
if (builder_.empty()) {
|
||||
@@ -222,73 +222,6 @@ std::string IfcCharacterDecoder::get(size_t& ptr) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void IfcCharacterDecoder::skip() {
|
||||
unsigned int parse_state = 0;
|
||||
char current_char;
|
||||
unsigned int hex_count = 0;
|
||||
while ((current_char = stream_->peek()) != 0) {
|
||||
if (EXPECTS_CHARACTER(parse_state)) {
|
||||
parse_state = 0;
|
||||
} else if (current_char == '\'' && (parse_state == 0U)) {
|
||||
parse_state = APOSTROPHE;
|
||||
} else if (current_char == '\\' && (parse_state == 0U)) {
|
||||
parse_state = FIRST_SOLIDUS;
|
||||
} else if (current_char == '\\' && EXPECTS_SOLIDUS(parse_state)) {
|
||||
if (((parse_state & ALPHABET_DEFINITION) != 0U) ||
|
||||
((parse_state & IGNORED_DIRECTIVE) != 0U) ||
|
||||
((parse_state & ENDEXTENDED_0) != 0U)) {
|
||||
parse_state = hex_count = 0;
|
||||
} else if ((parse_state & ENCOUNTERED_HEX) != 0U) {
|
||||
parse_state += THIRD_SOLIDUS;
|
||||
parse_state -= ENCOUNTERED_HEX;
|
||||
} else {
|
||||
parse_state += SECOND_SOLIDUS;
|
||||
}
|
||||
} else if (current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state)) {
|
||||
parse_state += ENDEXTENDED_X;
|
||||
} else if (current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state)) {
|
||||
parse_state += ENDEXTENDED_0;
|
||||
} else if (current_char == 'X' && EXPECTS_ARBITRARY(parse_state)) {
|
||||
parse_state += ARBITRARY;
|
||||
} else if (current_char == '2' && EXPECTS_ARBITRARY2(parse_state)) {
|
||||
parse_state += EXTENDED2;
|
||||
} else if (current_char == '4' && EXPECTS_ARBITRARY2(parse_state)) {
|
||||
parse_state += EXTENDED2 + EXTENDED4;
|
||||
} else if (current_char == 'P' && EXPECTS_ALPHABET(parse_state)) {
|
||||
parse_state += ALPHABET;
|
||||
} else if ((current_char == 'N' || current_char == 'F') && EXPECTS_N_OR_F(parse_state)) {
|
||||
parse_state += IGNORED_DIRECTIVE;
|
||||
} else if (IS_VALID_ALPHABET_DEFINITION(current_char) && EXPECTS_ALPHABET_DEFINITION(parse_state)) {
|
||||
parse_state += ALPHABET_DEFINITION;
|
||||
} else if (current_char == 'S' && EXPECTS_PAGE(parse_state)) {
|
||||
parse_state += PAGE;
|
||||
} else if (IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state)) {
|
||||
parse_state += HEX((++hex_count));
|
||||
if ((hex_count == 2 && ((parse_state & EXTENDED2) == 0U)) ||
|
||||
(hex_count == 4 && ((parse_state & EXTENDED4) == 0U)) ||
|
||||
(hex_count == 8)) {
|
||||
if (hex_count == 2) {
|
||||
parse_state = 0;
|
||||
} else {
|
||||
CLEAR_HEX(parse_state);
|
||||
parse_state |= ENCOUNTERED_HEX;
|
||||
}
|
||||
hex_count = 0;
|
||||
}
|
||||
} else if ((parse_state != 0U) && !(
|
||||
(current_char == '\\' && parse_state == FIRST_SOLIDUS) ||
|
||||
(current_char == '\'' && parse_state == APOSTROPHE))) {
|
||||
if (parse_state == APOSTROPHE && current_char != '\'') {
|
||||
break;
|
||||
}
|
||||
throw IfcInvalidTokenException(stream_->tell(), current_char);
|
||||
} else {
|
||||
parse_state = hex_count = 0;
|
||||
}
|
||||
stream_->increment();
|
||||
}
|
||||
}
|
||||
|
||||
IfcCharacterDecoder::ConversionMode IfcCharacterDecoder::mode = IfcCharacterDecoder::UTF8;
|
||||
char IfcCharacterDecoder::substitution_character = '_';
|
||||
|
||||
|
||||
@@ -54,9 +54,6 @@ class IFC_PARSE_API IfcCharacterDecoder {
|
||||
static char substitution_character;
|
||||
IfcCharacterDecoder(IfcParse::FileReader* stream);
|
||||
~IfcCharacterDecoder();
|
||||
// Only advances the underlying token stream read pointer
|
||||
// to the next token.
|
||||
void skip();
|
||||
// Gets a decoded string representation at the token stream
|
||||
// read pointer and advances the underlying token stream.
|
||||
operator std::string();
|
||||
|
||||
+29
-27
@@ -57,32 +57,33 @@ namespace {
|
||||
|
||||
template <typename Fn>
|
||||
void dispatch_token(std::optional<size_t> instance_id, int attribute_id, IfcParse::Token t, IfcParse::declaration* decl, Fn fn) {
|
||||
if (t.type == IfcParse::Token_BINARY) {
|
||||
fn(IfcParse::TokenFunc::asBinary(t));
|
||||
} else if (IfcParse::TokenFunc::isBool(t)) {
|
||||
fn(IfcParse::TokenFunc::asBool(t));
|
||||
} else if (IfcParse::TokenFunc::isLogical(t)) {
|
||||
fn(IfcParse::TokenFunc::asLogical(t));
|
||||
} else if (t.type == IfcParse::Token_ENUMERATION) {
|
||||
auto& s = IfcParse::TokenFunc::asStringRef(t);
|
||||
if (t.is_binary()) {
|
||||
fn(t.as_binary());
|
||||
} else if (t.is_bool()) {
|
||||
fn(t.as_bool());
|
||||
} else if (t.is_logical()) {
|
||||
fn(t.as_logical());
|
||||
} else if (t.is_enumeration()) {
|
||||
const auto& s = t.as_string();
|
||||
if (decl && decl->as_enumeration_type()) {
|
||||
try {
|
||||
fn(EnumerationReference(decl->as_enumeration_type(), decl->as_enumeration_type()->lookup_enum_offset(s)));
|
||||
} catch (IfcParse::IfcException& e) {
|
||||
Logger::Error("An enumeration literal '" + s + "' is not valid for type '" + decl->name() + "' at offset " + std::to_string(t.startPos));
|
||||
Logger::Error("An enumeration literal '" + s + "' is not valid for type '" + decl->name() + "' at offset " + std::to_string(t.start_pos));
|
||||
}
|
||||
} else {
|
||||
Logger::Error("An enumeration literal '" + s + "' is not expected at attribute index '" + std::to_string(attribute_id) + "' at offset " + std::to_string(t.startPos));
|
||||
Logger::Error("An enumeration literal '" + s + "' is not expected at attribute index '" + std::to_string(attribute_id) + "' at offset " + std::to_string(t.start_pos));
|
||||
}
|
||||
} else if (t.type == IfcParse::Token_FLOAT) {
|
||||
fn(IfcParse::TokenFunc::asFloat(t));
|
||||
} else if (t.type == IfcParse::Token_IDENTIFIER) {
|
||||
fn(IfcParse::reference_or_simple_type{ IfcParse::InstanceReference{ IfcParse::TokenFunc::asIdentifier(t), t.startPos } });
|
||||
} else if (t.type == IfcParse::Token_INT) {
|
||||
fn(IfcParse::TokenFunc::asInt(t));
|
||||
} else if (t.type == IfcParse::Token_STRING) {
|
||||
fn(IfcParse::TokenFunc::asStringRef(t));
|
||||
} else if (t.type == IfcParse::Token_OPERATOR && t.value_char == '*') {
|
||||
} else if (t.is_int()) {
|
||||
// @nb make sure is_int() comes before is_float()
|
||||
fn(t.as_int());
|
||||
} else if (t.is_float()) {
|
||||
fn(t.as_float());
|
||||
} else if (t.is_identifier()) {
|
||||
fn(IfcParse::reference_or_simple_type{IfcParse::InstanceReference{(int) t.as_identifier(), t.start_pos}});
|
||||
} else if (t.is_string()) {
|
||||
fn(t.as_string());
|
||||
} else if (t.is_operator('*')) {
|
||||
// This is only in place for the validator
|
||||
fn(Derived{});
|
||||
}
|
||||
@@ -681,22 +682,22 @@ std::optional<std::tuple<size_t, const IfcParse::declaration*, std::shared_ptr<I
|
||||
|
||||
unsigned current_id = 0;
|
||||
while (good_ && !lexer_->stream->eof() && !current_id) {
|
||||
if (token_stream_[0].type == IfcParse::Token_IDENTIFIER &&
|
||||
token_stream_[1].type == IfcParse::Token_OPERATOR &&
|
||||
if (token_stream_[0].type == IfcParse::Token::Token_IDENTIFIER &&
|
||||
token_stream_[1].type == IfcParse::Token::Token_OPERATOR &&
|
||||
token_stream_[1].value_char == '=' &&
|
||||
token_stream_[2].type == IfcParse::Token_KEYWORD) {
|
||||
current_id = (unsigned)TokenFunc::asIdentifier(token_stream_[0]);
|
||||
token_stream_[2].type == IfcParse::Token::Token_KEYWORD) {
|
||||
current_id = token_stream_[0].as_identifier();
|
||||
const IfcParse::declaration* entity_type;
|
||||
try {
|
||||
entity_type = schema_->declaration_by_name(TokenFunc::asStringRef(token_stream_[2]));
|
||||
entity_type = schema_->declaration_by_name(token_stream_[2].as_string());
|
||||
} catch (const IfcException& ex) {
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(ex.what()) + " at offset " + std::to_string(token_stream_[2].startPos));
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(ex.what()) + " at offset " + std::to_string(token_stream_[2].start_pos));
|
||||
current_id = 0;
|
||||
goto advance;
|
||||
}
|
||||
|
||||
if (entity_type->as_entity() == nullptr) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Non entity type " + entity_type->name() + " at offset " + std::to_string(token_stream_[2].startPos));
|
||||
Logger::Message(Logger::LOG_ERROR, "Non entity type " + entity_type->name() + " at offset " + std::to_string(token_stream_[2].start_pos));
|
||||
goto advance;
|
||||
}
|
||||
|
||||
@@ -745,7 +746,7 @@ std::optional<std::tuple<size_t, const IfcParse::declaration*, std::shared_ptr<I
|
||||
Logger::Message(Logger::LOG_ERROR, "Parsing terminated");
|
||||
}
|
||||
|
||||
if (!lexer_->stream->eof() && next_token.type == Token_NONE) {
|
||||
if (!lexer_->stream->eof() && !next_token) {
|
||||
good_ = file_open_status::INVALID_SYNTAX;
|
||||
break;
|
||||
}
|
||||
@@ -755,6 +756,7 @@ std::optional<std::tuple<size_t, const IfcParse::declaration*, std::shared_ptr<I
|
||||
|
||||
// Free pages in front of cursor when variable-width tokens are materialized into entity instance data objects
|
||||
(stream_ ? stream_ : (lexer_)->stream)->dropPages();
|
||||
lexer_->resetPool();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
+169
-254
@@ -153,6 +153,45 @@ size_t IfcSpfLexer::skipComment() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
std::string& IfcSpfLexer::getTempString() const {
|
||||
const size_t idx = pool_index++;
|
||||
const size_t slice = idx >> 4;
|
||||
const size_t offset = idx & 0xF;
|
||||
|
||||
while (stringpool_.size() <= slice) {
|
||||
stringpool_.push_back(std::make_unique<std::array<std::string, 16>>());
|
||||
}
|
||||
return (*stringpool_[slice])[offset];
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool parse_int_(const char* pStart, int& val) {
|
||||
char* pEnd;
|
||||
long result = strtol(pStart, &pEnd, 10);
|
||||
if (*pEnd != 0) {
|
||||
return false;
|
||||
}
|
||||
val = (int)result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_float_(const char* pStart, double& val) {
|
||||
char* pEnd;
|
||||
#ifdef _MSC_VER
|
||||
double result = _strtod_l(pStart, &pEnd, locale);
|
||||
#else
|
||||
double result = strtod_l(pStart, &pEnd, locale);
|
||||
#endif
|
||||
if (*pEnd != 0) {
|
||||
return false;
|
||||
}
|
||||
val = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// Returns the offset of the current Token and moves cursor to next
|
||||
//
|
||||
@@ -169,7 +208,6 @@ Token IfcSpfLexer::Next() {
|
||||
return Token{};
|
||||
}
|
||||
|
||||
auto& str = GetTempString();
|
||||
auto pos = stream->tell();
|
||||
char character = stream->read();
|
||||
|
||||
@@ -182,14 +220,30 @@ Token IfcSpfLexer::Next() {
|
||||
character == '$' ||
|
||||
character == '*')
|
||||
{
|
||||
return OperatorTokenPtr(this, pos, character);
|
||||
return Token(pos, character);
|
||||
}
|
||||
|
||||
auto& str = getTempString();
|
||||
|
||||
if (character == '\'') {
|
||||
// If a string is encountered defer processing to the IfcCharacterDecoder
|
||||
str = *decoder_;
|
||||
return Token(pos, Token::Token_STRING, str);
|
||||
} else {
|
||||
str.assign(&character, 1);
|
||||
auto ttype = Token::Token_NONE;
|
||||
if (character == '"' || character == '.') {
|
||||
if (character == '"') {
|
||||
ttype = Token::Token_BINARY;
|
||||
} else {
|
||||
ttype = Token::Token_ENUMERATION;
|
||||
}
|
||||
str.clear();
|
||||
} else if (character == '#') {
|
||||
ttype = Token::Token_IDENTIFIER;
|
||||
str.clear();
|
||||
} else {
|
||||
str.assign(&character, 1);
|
||||
}
|
||||
|
||||
while (!stream->eof()) {
|
||||
// Read character and increment pointer if not starting a new token
|
||||
@@ -203,267 +257,157 @@ Token IfcSpfLexer::Next() {
|
||||
break;
|
||||
}
|
||||
if (!(character == ' ' || character == '\r' || character == '\n' || character == '\t')) {
|
||||
str.push_back(character);
|
||||
if ((ttype == Token::Token_BINARY && character == '"') || (ttype == Token::Token_ENUMERATION && character == '.')) {
|
||||
// Skip
|
||||
} else {
|
||||
str.push_back(character);
|
||||
}
|
||||
}
|
||||
stream->increment();
|
||||
}
|
||||
}
|
||||
return GeneralTokenPtr(this, pos, str);
|
||||
}
|
||||
|
||||
//
|
||||
// Reads a std::string from the file at specified offset
|
||||
// Omits whitespace and comments
|
||||
//
|
||||
void IfcSpfLexer::TokenString(size_t offset, std::string& buffer) {
|
||||
buffer.clear();
|
||||
auto local_stream = *this->stream;
|
||||
local_stream.seek(offset);
|
||||
while (!local_stream.eof()) {
|
||||
char character = local_stream.peek();
|
||||
if (!buffer.empty() && (character == '(' ||
|
||||
character == ')' ||
|
||||
character == '=' ||
|
||||
character == ',' ||
|
||||
character == ';' ||
|
||||
character == '/')) {
|
||||
break;
|
||||
if (ttype == Token::Token_ENUMERATION && str.size() == 1 && (str[0] == 'T' || str[0] == 'F' || str[0] == 'U')) {
|
||||
popPoolEntry();
|
||||
return Token(pos, Token::Token_BOOL, str[0]);
|
||||
} else if (ttype == Token::Token_IDENTIFIER) {
|
||||
int int_val;
|
||||
if (!parse_int_(str.c_str(), int_val)) {
|
||||
throw IfcInvalidTokenException(pos, str, "instance name");
|
||||
}
|
||||
popPoolEntry();
|
||||
return Token(pos, ttype, int_val);
|
||||
} else if (ttype == Token::Token_NONE && !str.empty()) {
|
||||
int int_val;
|
||||
double float_val;
|
||||
auto& first = str.front();
|
||||
if ((first >= 'A' && first <= 'Z') || (first >= 'a' && first <= 'z')) {
|
||||
ttype = Token::Token_KEYWORD;
|
||||
return Token(pos, ttype, str);
|
||||
} else if (parse_int_(str.c_str(), int_val)) {
|
||||
ttype = Token::Token_INT;
|
||||
popPoolEntry();
|
||||
return Token(pos, ttype, int_val);
|
||||
} else if (parse_float_(str.c_str(), float_val)) {
|
||||
ttype = Token::Token_FLOAT;
|
||||
popPoolEntry();
|
||||
return Token(pos, float_val);
|
||||
}
|
||||
} else if (ttype == Token::Token_BINARY || ttype == Token::Token_ENUMERATION) {
|
||||
return Token(pos, ttype, str);
|
||||
}
|
||||
local_stream.increment();
|
||||
if (character == ' ' ||
|
||||
character == '\r' ||
|
||||
character == '\n' ||
|
||||
character == '\t') {
|
||||
continue;
|
||||
}
|
||||
if (character == '\'') {
|
||||
// todo, make decoder use local offset ptr
|
||||
auto local_offset = local_stream.tell();
|
||||
buffer = decoder_->get(local_offset);
|
||||
break;
|
||||
}
|
||||
buffer.push_back(character);
|
||||
|
||||
throw IfcInvalidTokenException(pos, str, "valid token");
|
||||
}
|
||||
}
|
||||
|
||||
//Note: according to STEP standard, there may be newlines in tokens
|
||||
/*
|
||||
inline void RemoveTokenSeparators(FileReader* stream, size_t start, size_t end, std::string& oDestination) {
|
||||
oDestination.clear();
|
||||
for (unsigned i = start; i < end; i++) {
|
||||
char character = stream->get(i);
|
||||
if (character == ' ' ||
|
||||
character == '\r' ||
|
||||
character == '\n' ||
|
||||
character == '\t') {
|
||||
continue;
|
||||
}
|
||||
oDestination += character;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
bool ParseInt(const char* pStart, int& val) {
|
||||
char* pEnd;
|
||||
long result = strtol(pStart, &pEnd, 10);
|
||||
if (*pEnd != 0) {
|
||||
return false;
|
||||
}
|
||||
val = (int)result;
|
||||
return true;
|
||||
bool Token::is_operator() {
|
||||
return type == Token_OPERATOR;
|
||||
}
|
||||
|
||||
bool ParseFloat(const char* pStart, double& val) {
|
||||
char* pEnd;
|
||||
#ifdef _MSC_VER
|
||||
double result = _strtod_l(pStart, &pEnd, locale);
|
||||
#else
|
||||
double result = strtod_l(pStart, &pEnd, locale);
|
||||
#endif
|
||||
if (*pEnd != 0) {
|
||||
return false;
|
||||
}
|
||||
val = result;
|
||||
return true;
|
||||
bool Token::is_operator(char character) {
|
||||
return type == Token_OPERATOR && value_char == character;
|
||||
}
|
||||
|
||||
bool ParseBool(const char* pStart, int& val) {
|
||||
if (strlen(pStart) != 3 || pStart[0] != '.' || pStart[2] != '.') {
|
||||
return false;
|
||||
}
|
||||
char mid = pStart[1];
|
||||
|
||||
if (mid == 'T') {
|
||||
val = 1;
|
||||
} else if (mid == 'F') {
|
||||
val = 0;
|
||||
} else if (mid == 'U') {
|
||||
val = 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
bool Token::is_identifier() {
|
||||
return type == Token_IDENTIFIER;
|
||||
}
|
||||
|
||||
Token IfcParse::OperatorTokenPtr(IfcSpfLexer* lexer, size_t start, char data) {
|
||||
Token token(lexer, start, Token_OPERATOR);
|
||||
token.value_char = data;
|
||||
return token;
|
||||
bool Token::is_string() {
|
||||
return type == Token_STRING;
|
||||
}
|
||||
|
||||
Token IfcParse::GeneralTokenPtr(IfcSpfLexer* lexer, size_t start, const std::string& tokenStr) {
|
||||
Token token(lexer, start, Token_NONE);
|
||||
|
||||
//determine type of the token
|
||||
const char& first = tokenStr.front();
|
||||
if (first == '#') {
|
||||
token.type = Token_IDENTIFIER;
|
||||
if (!ParseInt(tokenStr.c_str() + 1, token.value_int)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Token '" + tokenStr + "' at offset " + std::to_string(token.startPos) + " is not valid");
|
||||
token.type = Token_OPERATOR;
|
||||
token.value_char = '$';
|
||||
}
|
||||
} else if (first == '\'') {
|
||||
token.type = Token_STRING;
|
||||
} else if (first == '.') {
|
||||
token.type = Token_ENUMERATION;
|
||||
if (ParseBool(tokenStr.c_str(), token.value_int)) { //bool is also enumeration
|
||||
token.type = Token_BOOL;
|
||||
}
|
||||
} else if (first == '"') {
|
||||
token.type = Token_BINARY;
|
||||
} else if (ParseInt(tokenStr.c_str(), token.value_int)) {
|
||||
token.type = Token_INT;
|
||||
} else if (ParseFloat(tokenStr.c_str(), token.value_double)) {
|
||||
token.type = Token_FLOAT;
|
||||
} else {
|
||||
token.type = Token_KEYWORD;
|
||||
}
|
||||
|
||||
return token;
|
||||
bool Token::is_enumeration() {
|
||||
// @nb this is a bit confusing?
|
||||
return type == Token_ENUMERATION || type == Token_BOOL;
|
||||
}
|
||||
|
||||
bool TokenFunc::isOperator(const Token& token) {
|
||||
return token.type == Token_OPERATOR;
|
||||
bool Token::is_binary() {
|
||||
return type == Token_BINARY;
|
||||
}
|
||||
|
||||
bool TokenFunc::isOperator(const Token& token, char character) {
|
||||
return token.type == Token_OPERATOR && token.value_char == character;
|
||||
bool Token::is_keyword() {
|
||||
return type == Token_KEYWORD;
|
||||
}
|
||||
|
||||
bool TokenFunc::isIdentifier(const Token& token) {
|
||||
return token.type == Token_IDENTIFIER;
|
||||
bool Token::is_int() {
|
||||
return type == Token_INT;
|
||||
}
|
||||
|
||||
bool TokenFunc::isString(const Token& token) {
|
||||
return token.type == Token_STRING;
|
||||
bool Token::is_bool() {
|
||||
// Bool and logical share the same storage type, just logical unknown is stored as 'U'.
|
||||
return type == Token_BOOL && value_char != 'U';
|
||||
}
|
||||
|
||||
bool TokenFunc::isEnumeration(const Token& token) {
|
||||
return token.type == Token_ENUMERATION || token.type == Token_BOOL;
|
||||
bool Token::is_logical() {
|
||||
return type == Token_BOOL;
|
||||
}
|
||||
|
||||
bool TokenFunc::isBinary(const Token& token) {
|
||||
return token.type == Token_BINARY;
|
||||
}
|
||||
|
||||
bool TokenFunc::isKeyword(const Token& token) {
|
||||
return token.type == Token_KEYWORD;
|
||||
}
|
||||
|
||||
bool TokenFunc::isInt(const Token& token) {
|
||||
return token.type == Token_INT;
|
||||
}
|
||||
|
||||
bool TokenFunc::isBool(const Token& token) {
|
||||
// Bool and logical share the same storage type, just logical unknown is stored as 2.
|
||||
return token.type == Token_BOOL && token.value_int != 2;
|
||||
}
|
||||
|
||||
bool TokenFunc::isLogical(const Token& token) {
|
||||
return token.type == Token_BOOL;
|
||||
}
|
||||
|
||||
bool TokenFunc::isFloat(const Token& token) {
|
||||
bool Token::is_float() {
|
||||
#ifdef PERMISSIVE_FLOAT
|
||||
/// NB: We are being more permissive here then allowed by the standard
|
||||
return token.type == Token_FLOAT || token.type == Token_INT;
|
||||
return type == Token_FLOAT || type == Token_INT;
|
||||
#else
|
||||
return token.type == Token_FLOAT;
|
||||
return type == Token_FLOAT;
|
||||
#endif
|
||||
}
|
||||
|
||||
int TokenFunc::asInt(const Token& token) {
|
||||
if (token.type != Token_INT) {
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "integer");
|
||||
int Token::as_int() {
|
||||
if (type != Token_INT) {
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "integer");
|
||||
}
|
||||
return token.value_int;
|
||||
return value_int;
|
||||
}
|
||||
|
||||
int TokenFunc::asIdentifier(const Token& token) {
|
||||
if (token.type != Token_IDENTIFIER) {
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "instance name");
|
||||
unsigned Token::as_identifier() {
|
||||
if (type != Token_IDENTIFIER) {
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "instance name");
|
||||
}
|
||||
return token.value_int;
|
||||
return (unsigned) value_int;
|
||||
}
|
||||
|
||||
bool TokenFunc::asBool(const Token& token) {
|
||||
if (token.type != Token_BOOL) {
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "boolean");
|
||||
bool Token::as_bool() {
|
||||
if (type != Token_BOOL) {
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "boolean");
|
||||
}
|
||||
return token.value_int == 1;
|
||||
return value_char == 'T';
|
||||
}
|
||||
|
||||
boost::logic::tribool TokenFunc::asLogical(const Token& token) {
|
||||
if (token.type != Token_BOOL) {
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "boolean");
|
||||
boost::logic::tribool Token::as_logical() {
|
||||
if (type != Token_BOOL) {
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "logical");
|
||||
}
|
||||
if (token.value_int == 0) {
|
||||
if (value_int == 'F') {
|
||||
return false;
|
||||
}
|
||||
if (token.value_int == 1) {
|
||||
if (value_int == 'T') {
|
||||
return true;
|
||||
}
|
||||
return boost::logic::indeterminate;
|
||||
}
|
||||
|
||||
double TokenFunc::asFloat(const Token& token) {
|
||||
double Token::as_float() {
|
||||
#ifdef PERMISSIVE_FLOAT
|
||||
if (token.type == Token_INT) {
|
||||
if (type == Token_INT) {
|
||||
/// NB: We are being more permissive here then allowed by the standard
|
||||
return token.value_int;
|
||||
return value_int;
|
||||
} // ----> continues beyond preprocessor directive
|
||||
#endif
|
||||
if (token.type == Token_FLOAT) {
|
||||
return token.value_double;
|
||||
if (type == Token_FLOAT) {
|
||||
return value_double;
|
||||
}
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "real");
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "real");
|
||||
}
|
||||
|
||||
const std::string& TokenFunc::asStringRef(const Token& token) {
|
||||
if (token.type == Token_NONE) {
|
||||
throw IfcParse::IfcException("Null token encountered, premature end of file?");
|
||||
const std::string& Token::as_string() {
|
||||
if (is_string() || is_enumeration() || is_binary() || is_keyword()) {
|
||||
// @todo quotes
|
||||
return *value_string;
|
||||
}
|
||||
std::string& str = token.lexer->GetTempString();
|
||||
token.lexer->TokenString(token.startPos, str);
|
||||
if ((isString(token) || isEnumeration(token) || isBinary(token)) && !str.empty()) {
|
||||
//remove start+end characters in-place
|
||||
str.erase(str.end() - 1);
|
||||
str.erase(str.begin());
|
||||
}
|
||||
return str;
|
||||
throw IfcInvalidTokenException(start_pos, to_string(), "string");
|
||||
}
|
||||
|
||||
std::string TokenFunc::asString(const Token& token) {
|
||||
if (isString(token) || isEnumeration(token) || isBinary(token)) {
|
||||
return asStringRef(token);
|
||||
}
|
||||
throw IfcInvalidTokenException(token.startPos, toString(token), "string");
|
||||
}
|
||||
|
||||
boost::dynamic_bitset<> TokenFunc::asBinary(const Token& token) {
|
||||
const std::string& str = asStringRef(token);
|
||||
boost::dynamic_bitset<> Token::as_binary() {
|
||||
const std::string& str = as_string();
|
||||
if (str.empty()) {
|
||||
throw IfcException("Token is not a valid binary sequence");
|
||||
}
|
||||
@@ -475,7 +419,7 @@ boost::dynamic_bitset<> TokenFunc::asBinary(const Token& token) {
|
||||
}
|
||||
|
||||
++it;
|
||||
unsigned i = ((unsigned)str.size() - 1) * 4 - n;
|
||||
unsigned i = (str.size() - 1) * 4 - n;
|
||||
boost::dynamic_bitset<> bitset(i);
|
||||
|
||||
for (; it != str.end(); ++it) {
|
||||
@@ -494,26 +438,18 @@ boost::dynamic_bitset<> TokenFunc::asBinary(const Token& token) {
|
||||
return bitset;
|
||||
}
|
||||
|
||||
std::string TokenFunc::toString(const Token& token) {
|
||||
std::string Token::to_string() {
|
||||
std::string result;
|
||||
if (token.type == Token_OPERATOR) {
|
||||
result.push_back(token.value_char);
|
||||
} else if (token.type == Token_INT) {
|
||||
result = std::to_string(token.value_int);
|
||||
} else if (token.type == Token_BOOL) {
|
||||
if (token.value_int == 1) {
|
||||
result = ".T.";
|
||||
} else if (token.value_int == 0) {
|
||||
result = ".F.";
|
||||
} else {
|
||||
result = ".U.";
|
||||
}
|
||||
} else if (token.type == Token_FLOAT) {
|
||||
if (type == Token_OPERATOR || type == Token_BOOL) {
|
||||
result.push_back(value_char);
|
||||
} else if (type == Token_INT) {
|
||||
result = std::to_string(value_int);
|
||||
} else if (type == Token_FLOAT) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setprecision(15) << token.value_double;
|
||||
oss << std::setprecision(15) << value_double;
|
||||
result = oss.str();
|
||||
} else {
|
||||
token.lexer->TokenString(token.startPos, result);
|
||||
return as_string();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -525,34 +461,28 @@ std::string TokenFunc::toString(const Token& token) {
|
||||
void IfcParse::impl::in_memory_file_storage::load(std::optional<size_t> entity_instance_name, const IfcParse::entity* entity, parse_context& context, int attribute_index) {
|
||||
Token next = tokens->Next();
|
||||
|
||||
/*
|
||||
if (TokenFunc::isOperator(next, '(')) {
|
||||
next = tokens->Next();
|
||||
}
|
||||
*/
|
||||
|
||||
size_t attribute_index_within_data = 0;
|
||||
size_t return_value = 0;
|
||||
|
||||
while ((next.startPos != 0U) || (next.lexer != nullptr)) {
|
||||
if (TokenFunc::isOperator(next, ',')) {
|
||||
while (next) {
|
||||
if (next.is_operator(',')) {
|
||||
if (attribute_index == -1) {
|
||||
attribute_index_within_data += 1;
|
||||
}
|
||||
} else if (TokenFunc::isOperator(next, ')')) {
|
||||
} else if (next.is_operator(')')) {
|
||||
break;
|
||||
} else if (TokenFunc::isOperator(next, '(')) {
|
||||
} else if (next.is_operator('(')) {
|
||||
return_value++;
|
||||
load(entity_instance_name, entity, context.push(), attribute_index == -1 ? (int) attribute_index_within_data : attribute_index);
|
||||
} else {
|
||||
return_value++;
|
||||
if (TokenFunc::isIdentifier(next) && entity && entity_instance_name) {
|
||||
if (next.is_identifier() && entity && entity_instance_name) {
|
||||
register_inverse(*entity_instance_name, entity, next.value_int, attribute_index == -1 ? (int) attribute_index_within_data : attribute_index);
|
||||
}
|
||||
|
||||
if (TokenFunc::isKeyword(next)) {
|
||||
if (next.is_keyword()) {
|
||||
try {
|
||||
const auto* decl = (schema ? schema : file->schema())->declaration_by_name(TokenFunc::asStringRef(next));
|
||||
const auto* decl = (schema ? schema : file->schema())->declaration_by_name(next.as_string());
|
||||
parse_context ps;
|
||||
tokens->Next();
|
||||
// The only case we know where a defined type contains entity
|
||||
@@ -568,7 +498,7 @@ void IfcParse::impl::in_memory_file_storage::load(std::optional<size_t> entity_i
|
||||
// @todo do we need express::Base here? Or should we just push InstanceData?
|
||||
context.push(simple_type_instance);
|
||||
} catch (IfcException& e) {
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + " at offset " + std::to_string(next.startPos));
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + " at offset " + std::to_string(next.start_pos));
|
||||
// #4070 We didn't actually capture an aggregate entry, undo length increment.
|
||||
return_value--;
|
||||
}
|
||||
@@ -580,25 +510,10 @@ void IfcParse::impl::in_memory_file_storage::load(std::optional<size_t> entity_i
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Reads an Entity from the list of Tokens at the specified offset in the file
|
||||
//
|
||||
std::shared_ptr<InstanceData> IfcParse::impl::in_memory_file_storage::read(unsigned int i) {
|
||||
Token datatype = tokens->Next();
|
||||
if (!TokenFunc::isKeyword(datatype)) {
|
||||
throw IfcException("Unexpected token while parsing entity");
|
||||
}
|
||||
const IfcParse::declaration* ty = file->schema()->declaration_by_name(TokenFunc::asStringRef(datatype));
|
||||
parse_context pc;
|
||||
tokens->Next();
|
||||
load(i, ty->as_entity(), pc, -1);
|
||||
return pc.construct(file, i, *references_to_resolve, ty, std::nullopt, -1);
|
||||
}
|
||||
|
||||
void IfcParse::impl::in_memory_file_storage::try_read_semicolon() const {
|
||||
auto old_offset = tokens->stream->tell();
|
||||
Token semilocon = tokens->Next();
|
||||
if (!TokenFunc::isOperator(semilocon, ';')) {
|
||||
if (!semilocon.is_operator(';')) {
|
||||
tokens->stream->seek(old_offset);
|
||||
}
|
||||
}
|
||||
@@ -1354,8 +1269,8 @@ bool IfcParse::InstanceStreamer::hasSemicolon() const {
|
||||
} catch (const std::out_of_range&) {
|
||||
return false;
|
||||
}
|
||||
while (t.type != Token_NONE) {
|
||||
if (TokenFunc::isOperator(t, ';')) {
|
||||
while (t.type != Token::Token_NONE) {
|
||||
if (t.is_operator(';')) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
@@ -1378,8 +1293,8 @@ size_t IfcParse::InstanceStreamer::semicolonCount() const {
|
||||
} catch (const std::out_of_range&) {
|
||||
return false;
|
||||
}
|
||||
while (t.type != Token_NONE) {
|
||||
if (TokenFunc::isOperator(t, ';')) {
|
||||
while (t.type != Token::Token_NONE) {
|
||||
if (t.is_operator(';')) {
|
||||
count++;
|
||||
}
|
||||
try {
|
||||
|
||||
+13
-62
@@ -47,64 +47,6 @@ extern const char *IFCOPENSHELL_VERSION;
|
||||
|
||||
namespace IfcParse {
|
||||
|
||||
/// Provides functions to convert Tokens to binary data
|
||||
/// Tokens are merely offsets to where they can be read in the file
|
||||
class IFC_PARSE_API TokenFunc {
|
||||
private:
|
||||
static bool startsWith(const Token& token, char character);
|
||||
|
||||
public:
|
||||
/// Returns the offset at which the token is read from the file
|
||||
// static unsigned int Offset(const Token& t);
|
||||
/// Returns whether the token can be interpreted as a string
|
||||
static bool isString(const Token& token);
|
||||
/// Returns whether the token can be interpreted as an identifier
|
||||
static bool isIdentifier(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a syntactical operator
|
||||
static bool isOperator(const Token& token);
|
||||
/// Returns whether the token is a given operator
|
||||
static bool isOperator(const Token& token, char character);
|
||||
/// Returns whether the token can be interpreted as an enumerated value
|
||||
static bool isEnumeration(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a datatype name
|
||||
static bool isKeyword(const Token& token);
|
||||
/// Returns whether the token can be interpreted as an integer
|
||||
static bool isInt(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a boolean
|
||||
static bool isBool(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a logical
|
||||
static bool isLogical(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a floating point number
|
||||
static bool isFloat(const Token& token);
|
||||
/// Returns whether the token can be interpreted as a binary type
|
||||
static bool isBinary(const Token& token);
|
||||
/// Returns the token interpreted as an integer
|
||||
static int asInt(const Token& token);
|
||||
/// Returns the token interpreted as an identifier
|
||||
static int asIdentifier(const Token& token);
|
||||
/// Returns the token interpreted as an boolean (.T. or .F.)
|
||||
static bool asBool(const Token& token);
|
||||
/// Returns the token interpreted as an logical (.T. or .F. or .U.)
|
||||
static boost::logic::tribool asLogical(const Token& token);
|
||||
/// Returns the token as a floating point number
|
||||
static double asFloat(const Token& token);
|
||||
/// Returns the token as a string (without the dot or apostrophe)
|
||||
static std::string asString(const Token& token);
|
||||
/// Returns the token as a string in internal buffer (for optimization purposes)
|
||||
static const std::string& asStringRef(const Token& token);
|
||||
/// Returns the token as a string (without the dot or apostrophe)
|
||||
static boost::dynamic_bitset<> asBinary(const Token& token);
|
||||
/// Returns a string representation of the token (including the dot or apostrophe)
|
||||
static std::string toString(const Token& token);
|
||||
};
|
||||
|
||||
//
|
||||
// Functions for creating Tokens from an arbitary file offset
|
||||
// The first 4 bits are reserved for Tokens of type ()=,;$*
|
||||
//
|
||||
Token OperatorTokenPtr(IfcSpfLexer* tokens, size_t start, char data);
|
||||
Token GeneralTokenPtr(IfcSpfLexer* tokens, size_t start, const std::string& data);
|
||||
|
||||
/// A stream of tokens to be read from a FileReader.
|
||||
class IFC_PARSE_API IfcSpfLexer {
|
||||
private:
|
||||
@@ -112,17 +54,26 @@ class IFC_PARSE_API IfcSpfLexer {
|
||||
size_t skipWhitespace() const;
|
||||
size_t skipComment() const;
|
||||
|
||||
mutable std::vector<std::unique_ptr<std::array<std::string, 16>>> stringpool_;
|
||||
mutable size_t pool_index = 0;
|
||||
|
||||
public:
|
||||
std::string& GetTempString() const {
|
||||
static thread_local std::string string;
|
||||
return string;
|
||||
std::string& getTempString() const;
|
||||
void resetPool() const {
|
||||
pool_index = 0;
|
||||
}
|
||||
void popPoolEntry() {
|
||||
if (pool_index > 0) {
|
||||
--pool_index;
|
||||
}
|
||||
}
|
||||
|
||||
FileReader* stream;
|
||||
// IfcFile* file;
|
||||
IfcSpfLexer(FileReader* stream);
|
||||
Token Next();
|
||||
~IfcSpfLexer();
|
||||
void TokenString(size_t offset, std::string& result);
|
||||
// void TokenString(size_t offset, std::string& result);
|
||||
};
|
||||
|
||||
IFC_PARSE_API std::vector<express::Base> traverse(const express::Base& instance, int max_level = -1);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace {
|
||||
|
||||
void IfcSpfHeader::readSemicolon() {
|
||||
if (storage_ != nullptr) {
|
||||
if (!TokenFunc::isOperator(storage_->tokens->Next(), ';')) {
|
||||
if (!storage_->tokens->Next().is_operator(';')) {
|
||||
throw IfcException(std::string("Expected ;"));
|
||||
}
|
||||
} else {
|
||||
@@ -55,7 +55,7 @@ void IfcSpfHeader::readSemicolon() {
|
||||
|
||||
void IfcSpfHeader::readTerminal(const std::string& term, Trail trail) {
|
||||
if (storage_ != nullptr) {
|
||||
if (TokenFunc::asStringRef(storage_->tokens->Next()) != term) {
|
||||
if (storage_->tokens->Next().as_string() != term) {
|
||||
throw IfcException(std::string("Expected " + term));
|
||||
}
|
||||
if (trail == TRAILING_SEMICOLON) {
|
||||
|
||||
+57
-25
@@ -134,37 +134,71 @@ namespace IfcParse {
|
||||
class IfcSpfLexer;
|
||||
class FileReader;
|
||||
|
||||
enum TokenType {
|
||||
Token_NONE,
|
||||
Token_STRING,
|
||||
Token_IDENTIFIER,
|
||||
Token_OPERATOR,
|
||||
Token_ENUMERATION,
|
||||
Token_KEYWORD,
|
||||
Token_INT,
|
||||
Token_BOOL,
|
||||
Token_FLOAT,
|
||||
Token_BINARY
|
||||
};
|
||||
|
||||
struct Token {
|
||||
IfcSpfLexer* lexer; //TODO: remove it from here
|
||||
size_t startPos;
|
||||
enum TokenType {
|
||||
Token_NONE,
|
||||
Token_STRING,
|
||||
Token_IDENTIFIER,
|
||||
Token_OPERATOR,
|
||||
Token_ENUMERATION,
|
||||
Token_KEYWORD,
|
||||
Token_INT,
|
||||
Token_BOOL,
|
||||
Token_FLOAT,
|
||||
Token_BINARY
|
||||
};
|
||||
|
||||
size_t start_pos;
|
||||
TokenType type;
|
||||
|
||||
union {
|
||||
char value_char; //types: OPERATOR
|
||||
int value_int; //types: INT, IDENTIFIER
|
||||
double value_double; //types: FLOAT
|
||||
const std::string* value_string; //types: STR, ENUM, KEYWORD; lifetime managed by IfcSpfLexer::string_pool_
|
||||
};
|
||||
|
||||
Token() : lexer(0),
|
||||
startPos(0),
|
||||
type(Token_NONE) {
|
||||
}
|
||||
Token(IfcSpfLexer* _lexer, size_t _startPos, TokenType _type)
|
||||
: lexer(_lexer),
|
||||
startPos(_startPos),
|
||||
type(_type) {
|
||||
Token() : start_pos(0),
|
||||
type(Token_NONE) {}
|
||||
|
||||
Token(size_t start, TokenType ty, const std::string& str)
|
||||
: start_pos(start), type(ty), value_string(&str) {}
|
||||
|
||||
Token(size_t start, TokenType ty, int i)
|
||||
: start_pos(start), type(ty), value_int(i) {}
|
||||
|
||||
Token(size_t start, double d)
|
||||
: start_pos(start), type(Token_FLOAT), value_double(d) {}
|
||||
|
||||
Token(size_t start, char op)
|
||||
: start_pos(start), type(Token_OPERATOR), value_char(op) {}
|
||||
|
||||
Token(size_t start, TokenType ty, char c)
|
||||
: start_pos(start), type(ty), value_char(c) {}
|
||||
|
||||
bool is_string();
|
||||
bool is_identifier();
|
||||
bool is_operator();
|
||||
bool is_operator(char character);
|
||||
bool is_enumeration();
|
||||
bool is_keyword();
|
||||
bool is_int();
|
||||
bool is_bool();
|
||||
bool is_logical();
|
||||
bool is_float();
|
||||
bool is_binary();
|
||||
|
||||
int as_int();
|
||||
unsigned as_identifier();
|
||||
bool as_bool();
|
||||
boost::logic::tribool as_logical();
|
||||
double as_float();
|
||||
const std::string& as_string();
|
||||
boost::dynamic_bitset<> as_binary();
|
||||
std::string to_string();
|
||||
|
||||
operator bool() const {
|
||||
return type != Token_NONE;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -275,8 +309,6 @@ namespace IfcParse {
|
||||
void register_inverse(unsigned, const IfcParse::entity* from_entity, int inst_id, int attribute_index);
|
||||
void unregister_inverse(unsigned, const IfcParse::entity* from_entity, const express::Base&, int attribute_index);
|
||||
|
||||
// @todo is this still used
|
||||
std::shared_ptr<InstanceData> read(unsigned int index);
|
||||
void read_from_stream(IfcParse::FileReader* stream, const IfcParse::schema_definition*& schema, unsigned int& max_id, const std::set<std::string>& typed_to_bypass);
|
||||
|
||||
file_open_status good_ = file_open_status::SUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user