Merge pull request #935 from joakim-hove/parse-python

Started to parse Python code
This commit is contained in:
Joakim Hove
2019-09-04 23:09:43 +02:00
committed by GitHub
16 changed files with 549 additions and 81 deletions

View File

@@ -253,6 +253,7 @@ if(ENABLE_ECL_INPUT)
tests/parser/ParseContextTests.cpp
tests/parser/ParseContext_EXIT1.cpp
tests/parser/ParseDATAWithDefault.cpp
tests/parser/PYACTION.cpp
tests/parser/PORVTests.cpp
tests/parser/RawKeywordTests.cpp
tests/parser/ResinsightTest.cpp

View File

@@ -123,6 +123,8 @@ namespace Opm {
const ParseContext& context,
ErrorGuard& errors);
const std::vector<std::pair<std::string,std::string>> codeKeywords() const;
private:
// associative map of the parser internal name and the corresponding ParserKeyword object
std::vector< std::unique_ptr< const ParserKeyword > > keyword_storage;
@@ -132,6 +134,8 @@ namespace Opm {
// ParserKeyword object for keywords which match a regular expression
std::map< string_view, const ParserKeyword* > m_wildCardKeywords;
std::vector<std::pair<std::string,std::string>> code_keywords;
bool hasWildCardKeyword(const std::string& keyword) const;
const ParserKeyword* matchingKeyword(const string_view& keyword) const;

View File

@@ -28,7 +28,8 @@ namespace Opm {
SLASH_TERMINATED = 0,
FIXED = 1,
OTHER_KEYWORD_IN_DECK = 2,
UNKNOWN=3
UNKNOWN = 3,
FIXED_CODE = 4
};

View File

@@ -46,10 +46,8 @@ namespace Opm {
RAW_STRING and UDA.
DataType: This the C++ type of items generated when parsing the deck,
currently the available datatypes are int, double and std::string.
The mapping from input type to data type is many-to-one, and
currently both STRING and RAW_STRING map to std::string and both
DOUBLE and UDA map to double.
currently the available datatypes are int, double, std::string and
the user defined type UDAValue.
Splitting the type treatment in two layers in this way enables
properties/transformations to be added to the data before they are
@@ -65,7 +63,7 @@ namespace Opm {
static item_size size_from_string( const std::string& );
static std::string string_from_size( item_size );
enum class itype {UNKNOWN, DOUBLE, INT, STRING, RAW_STRING, UDA};
enum class itype {UNKNOWN, DOUBLE, INT, STRING, RAW_STRING, UDA, CODE};
static itype from_string(const std::string& string_value);
static std::string to_string(itype input_type);
std::string type_literal() const;

View File

@@ -113,6 +113,8 @@ namespace Opm {
bool hasMultipleDeckNames() const;
void clearDeckNames();
void addDeckName( const std::string& deckName );
void setCodeEnd(const std::string& end);
const std::string& codeEnd() const;
DeckNameSet::const_iterator deckNamesBegin() const;
DeckNameSet::const_iterator deckNamesEnd() const;
@@ -127,6 +129,7 @@ namespace Opm {
const KeywordSize& getKeywordSize() const;
bool isDataKeyword() const;
bool rawStringKeyword() const;
bool isCodeKeyword() const;
std::string createDeclaration(const std::string& indent) const;
std::string createDecl() const;
@@ -149,11 +152,13 @@ namespace Opm {
bool m_isTableCollection;
std::string m_Description;
bool raw_string_keyword = false;
std::string code_end;
static bool validNameStart(const string_view& name);
void initDeckNames( const Json::JsonObject& jsonConfig );
void initSectionNames( const Json::JsonObject& jsonConfig );
void initMatchRegex( const Json::JsonObject& jsonObject );
void initCode( const Json::JsonObject& jsonConfig );
void initData( const Json::JsonObject& jsonConfig );
void initSize( const Json::JsonObject& jsonConfig );
void initSizeKeyword(const Json::JsonObject& sizeObject);

View File

@@ -196,24 +196,84 @@ inline bool getline( string_view& input, string_view& line ) {
* everything after (terminating) slashes. Manually copying into the string for
* performance.
*/
inline std::string clean( const std::string& str ) {
inline std::string fast_clean( const std::string& str ) {
std::string dst;
dst.resize( str.size() );
string_view input( str ), line;
auto dsti = dst.begin();
while( getline( input, line ) ) {
line = trim( strip_comments(line));
while( true ) {
std::copy( line.begin(), line.end(), dsti );
dsti += std::distance( line.begin(), line.end() );
*dsti++ = '\n';
if ( getline( input, line ) ) {
line = trim( strip_comments(line));
std::copy( line.begin(), line.end(), dsti );
dsti += std::distance( line.begin(), line.end() );
*dsti++ = '\n';
} else
break;
}
dst.resize( std::distance( dst.begin(), dsti ) );
return dst;
}
inline std::string clean( const std::vector<std::pair<std::string, std::string>>& code_keywords, const std::string& str ) {
auto count = std::count_if(code_keywords.begin(), code_keywords.end(), [&str](const std::pair<std::string, std::string>& code_pair)
{
return str.find(code_pair.first) != std::string::npos;
});
if (count == 0)
return fast_clean(str);
else {
std::string dst;
dst.resize( str.size() );
string_view input( str ), line;
auto dsti = dst.begin();
while( true ) {
for (const auto& code_pair : code_keywords) {
const auto& keyword = code_pair.first;
if (input.starts_with(keyword)) {
std::string end_string = code_pair.second;
auto end_pos = input.find(end_string);
if (end_pos == std::string::npos) {
std::copy(input.begin(), input.end(), dsti);
dsti += std::distance( input.begin(), input.end() );
input = string_view(input.end(), input.end());
break;
} else {
end_pos += end_string.size();
std::copy(input.begin(), input.begin() + end_pos, dsti);
dsti += end_pos;
*dsti++ = '\n';
input = string_view(input.begin() + end_pos + 1, input.end());
break;
}
}
}
if ( getline( input, line ) ) {
line = trim( strip_comments(line));
std::copy( line.begin(), line.end(), dsti );
dsti += std::distance( line.begin(), line.end() );
*dsti++ = '\n';
} else
break;
}
dst.resize( std::distance( dst.begin(), dsti ) );
return dst;
}
}
inline std::string make_deck_name(const string_view& str) {
auto first_sep = std::find_if( str.begin(), str.end(), RawConsts::is_separator() );
return uppercase( str.substr(0, first_sep - str.begin()) );
@@ -238,7 +298,6 @@ inline bool isTerminatedRecordString(const string_view& line) {
}
struct file {
file( boost::filesystem::path p, const std::string& in ) :
input( in ), path( p )
@@ -266,8 +325,8 @@ void InputStack::push( std::string&& input, boost::filesystem::path p ) {
class ParserState {
public:
ParserState( const ParseContext&, ErrorGuard& );
ParserState( const ParseContext&, ErrorGuard&, boost::filesystem::path );
ParserState( const std::vector<std::pair<std::string,std::string>>&, const ParseContext&, ErrorGuard& );
ParserState( const std::vector<std::pair<std::string,std::string>>&, const ParseContext&, ErrorGuard&, boost::filesystem::path );
void loadString( const std::string& );
void loadFile( const boost::filesystem::path& );
@@ -286,11 +345,11 @@ class ParserState {
void closeFile();
private:
const std::vector<std::pair<std::string, std::string>> code_keywords;
InputStack input_stack;
std::map< std::string, std::string > pathMap;
boost::filesystem::path rootPath;
public:
ParserKeywordSizeEnum lastSizeType = SLASH_TERMINATED;
std::string lastKeyWord;
@@ -301,7 +360,6 @@ class ParserState {
bool unknown_keyword = false;
};
const boost::filesystem::path& ParserState::current_path() const {
return this->input_stack.top().path;
}
@@ -329,6 +387,7 @@ string_view ParserState::getline() {
}
void ParserState::ungetline(const string_view& line) {
auto& file_view = this->input_stack.top().input;
if (line.end() + 1 != file_view.begin())
@@ -345,14 +404,19 @@ void ParserState::closeFile() {
this->input_stack.pop();
}
ParserState::ParserState(const ParseContext& __parseContext, ErrorGuard& errors_arg) :
ParserState::ParserState(const std::vector<std::pair<std::string, std::string>>& code_keywords_arg,
const ParseContext& __parseContext,
ErrorGuard& errors_arg) :
code_keywords(code_keywords_arg),
parseContext( __parseContext ),
errors( errors_arg )
{}
ParserState::ParserState( const ParseContext& context,
ParserState::ParserState( const std::vector<std::pair<std::string, std::string>>& code_keywords_arg,
const ParseContext& context,
ErrorGuard& errors_arg,
boost::filesystem::path p ) :
code_keywords(code_keywords_arg),
rootPath( boost::filesystem::canonical( p ).parent_path() ),
parseContext( context ),
errors( errors_arg )
@@ -361,7 +425,7 @@ ParserState::ParserState( const ParseContext& context,
}
void ParserState::loadString(const std::string& input) {
this->input_stack.push( str::clean( input + "\n" ) );
this->input_stack.push( str::clean( this->code_keywords, input + "\n" ) );
}
void ParserState::loadFile(const boost::filesystem::path& inputFile) {
@@ -406,7 +470,7 @@ void ParserState::loadFile(const boost::filesystem::path& inputFile) {
throw std::runtime_error( "Error when reading input file '"
+ inputFileCanonical.string() + "'" );
this->input_stack.push( str::clean( buffer ), inputFileCanonical );
this->input_stack.push( str::clean( this->code_keywords, buffer ), inputFileCanonical );
}
/*
@@ -505,14 +569,18 @@ RawKeyword * newRawKeyword(const ParserKeyword& parserKeyword, const std::string
rawSizeType);
}
if( parserKeyword.hasFixedSize() )
if( parserKeyword.hasFixedSize() ) {
auto size_type = Raw::FIXED;
if (parserKeyword.isCodeKeyword())
size_type = Raw::CODE;
return new RawKeyword( keywordString,
parserState.current_path().string(),
parserState.line(),
raw_string_keyword,
Raw::FIXED,
size_type,
parserKeyword.getFixedSize());
}
const auto& keyword_size = parserKeyword.getKeywordSize();
const auto& deck = parserState.deck;
@@ -625,11 +693,26 @@ std::unique_ptr<RawKeyword> tryParseKeyword( ParserState& parserState, const Par
}
} else {
/* We are looking at some random gibberish?! */
if (!parserState.unknown_keyword) {
if (!parserState.unknown_keyword)
parserState.handleRandomText( line );
}
}
} else {
if (rawKeyword->getSizeType() == Raw::CODE) {
const auto& parserKeyword = parser.getParserKeywordFromDeckName(rawKeyword->getKeywordName());
auto end_pos = line.find(parserKeyword.codeEnd());
if (end_pos != std::string::npos) {
string_view line_content = { line.begin(), line.begin() + end_pos};
record_buffer = str::update_record_buffer( record_buffer, line_content );
RawRecord record(record_buffer, true);
rawKeyword->addRecord(record);
return rawKeyword;
} else
record_buffer = str::update_record_buffer( record_buffer.begin(), line );
continue;
}
if (rawKeyword->getSizeType() == Raw::UNKNOWN) {
/*
When we are spinning through a keyword of size type UNKNOWN it
@@ -655,7 +738,6 @@ std::unique_ptr<RawKeyword> tryParseKeyword( ParserState& parserState, const Par
line = str::del_after_slash(line, rawKeyword->rawStringKeyword());
record_buffer = str::update_record_buffer(record_buffer, line);
if (is_title) {
if (record_buffer.empty()) {
RawRecord record("opm/flow simulation");
@@ -681,17 +763,26 @@ std::unique_ptr<RawKeyword> tryParseKeyword( ParserState& parserState, const Par
record_buffer = str::emptystr;
}
}
}
if (rawKeyword) {
if (rawKeyword->getSizeType() == Raw::UNKNOWN)
rawKeyword->terminateKeyword();
if (!rawKeyword->isFinished())
if (!rawKeyword->isFinished()) {
/*
It is not necessary to explicitly terminate code keywords, in that
case they will load all the content until EOF is reached.
*/
if (rawKeyword->getSizeType() == Raw::CODE) {
RawRecord record(string_view{ record_buffer.begin(), record_buffer.end() + 1}, true);
rawKeyword->addRecord(record);
return rawKeyword;
}
throw std::invalid_argument("Keyword " + rawKeyword->getKeywordName() + " is not properly terminated");
}
}
return rawKeyword;
@@ -842,7 +933,7 @@ bool parseState( ParserState& parserState, const Parser& parser ) {
}
Deck Parser::parseFile(const std::string &dataFileName, const ParseContext& parseContext, ErrorGuard& errors) const {
ParserState parserState( parseContext, errors, dataFileName );
ParserState parserState( this->codeKeywords(), parseContext, errors, dataFileName );
parseState( parserState, *this );
applyUnitsToDeck( parserState.deck );
@@ -858,7 +949,7 @@ bool parseState( ParserState& parserState, const Parser& parser ) {
Deck Parser::parseString(const std::string &data, const ParseContext& parseContext, ErrorGuard& errors) const {
ParserState parserState( parseContext, errors );
ParserState parserState( this->codeKeywords(), parseContext, errors );
parserState.loadString( data );
parseState( parserState, *this );
@@ -932,6 +1023,8 @@ void Parser::addParserKeyword( std::unique_ptr< const ParserKeyword >&& parserKe
m_wildCardKeywords[ name ] = ptr;
}
if (ptr->isCodeKeyword())
this->code_keywords.emplace_back( ptr->getName(), ptr->codeEnd() );
}
@@ -1015,6 +1108,10 @@ std::vector<std::string> Parser::getAllDeckNames () const {
}
}
const std::vector<std::pair<std::string,std::string>> Parser::codeKeywords() const {
return this->code_keywords;
}
void Parser::applyUnitsToDeck(Deck& deck) const {
/*

View File

@@ -39,6 +39,9 @@ namespace Opm {
case UNKNOWN:
return "UNKNOWN";
break;
case FIXED_CODE:
return "FIXED_CODE";
break;
default:
throw std::invalid_argument("Implementation error - should NOT be here");
}
@@ -55,6 +58,8 @@ namespace Opm {
return OTHER_KEYWORD_IN_DECK;
else if (stringValue == "UNKNOWN")
return UNKNOWN;
else if (stringValue == "FIXED_CODE")
return FIXED_CODE;
else
throw std::invalid_argument("String: " + stringValue + " can not be converted to enum value");
}

View File

@@ -220,6 +220,8 @@ void ParserItem::setInputType(ParserItem::itype input_type_arg) {
else if (input_type == itype::UDA)
this->setDataType( UDAValue(0) );
else if (input_type == itype::CODE)
this->setDataType( std::string() );
else
throw std::invalid_argument("BUG: input type not recognized in setInputType()");
}

View File

@@ -39,6 +39,8 @@ namespace Opm {
void ParserKeyword::setSizeType( ParserKeywordSizeEnum sizeType ) {
m_keywordSizeType = sizeType;
if (sizeType == FIXED_CODE)
this->m_fixedSize = 1;
}
void ParserKeyword::setFixedSize( size_t keywordSize) {
@@ -102,6 +104,15 @@ namespace Opm {
m_Description = description;
}
void ParserKeyword::setCodeEnd(const std::string& end) {
this->code_end = end;
}
const std::string& ParserKeyword::codeEnd() const {
return this->code_end;
}
void ParserKeyword::initSize(const Json::JsonObject& jsonConfig) {
// The number of record has been set explicitly with the size: keyword
if (jsonConfig.has_item("size")) {
@@ -113,30 +124,35 @@ namespace Opm {
} else
initSizeKeyword(sizeObject);
} else {
if (jsonConfig.has_item("num_tables")) {
Json::JsonObject numTablesObject = jsonConfig.get_item("num_tables");
if (!numTablesObject.is_object())
throw std::invalid_argument("The num_tables key must point to a {} object");
initSizeKeyword(numTablesObject);
m_isTableCollection = true;
} else {
if (jsonConfig.has_item("items") || jsonConfig.has_item("records"))
// The number of records is undetermined - the keyword will be '/' terminated.
m_keywordSizeType = SLASH_TERMINATED;
else {
m_keywordSizeType = FIXED;
if (jsonConfig.has_item("data"))
m_fixedSize = 1;
else
m_fixedSize = 0;
}
}
return;
}
if (jsonConfig.has_item("num_tables")) {
Json::JsonObject numTablesObject = jsonConfig.get_item("num_tables");
if (!numTablesObject.is_object())
throw std::invalid_argument(
"The num_tables key must point to a {} object");
initSizeKeyword(numTablesObject);
m_isTableCollection = true;
return;
}
if (jsonConfig.has_item("items") || jsonConfig.has_item("records")) {
// The number of records is undetermined - the keyword will be '/'
// terminated.
m_keywordSizeType = SLASH_TERMINATED;
return;
} else {
m_keywordSizeType = FIXED;
m_fixedSize = 0;
}
}
ParserKeyword::ParserKeyword(const Json::JsonObject& jsonConfig) {
if (jsonConfig.has_item("name")) {
@@ -179,9 +195,11 @@ namespace Opm {
if (jsonConfig.has_item("data"))
initData(jsonConfig);
if (jsonConfig.has_item("description")) {
if (jsonConfig.has_item("code"))
this->initCode(jsonConfig);
if (jsonConfig.has_item("description"))
m_Description = jsonConfig.get_string("description");
}
}
@@ -331,6 +349,25 @@ void set_dimensions( ParserItem& item,
}
void ParserKeyword::initCode(const Json::JsonObject& jsonConfig) {
this->m_fixedSize = 1U;
this->m_keywordSizeType = FIXED_CODE;
const Json::JsonObject codeConfig = jsonConfig.get_item("code");
if (!codeConfig.has_item("end"))
throw std::invalid_argument("The end: is missing from the code block");
this->setCodeEnd(codeConfig.get_string("end"));
const std::string itemName("code");
auto input_type = ParserItem::itype::RAW_STRING;
ParserItem item( itemName, input_type);
ParserRecord record;
item.setSizeType( ParserItem::item_size::ALL );
record.addItem(item);
this->addRecord(record);
}
void ParserKeyword::initData(const Json::JsonObject& jsonConfig) {
this->m_fixedSize = 1U;
@@ -503,7 +540,7 @@ void set_dimensions( ParserItem& item,
}
bool ParserKeyword::hasFixedSize() const {
return m_keywordSizeType == FIXED;
return (this->m_keywordSizeType == FIXED || this->m_keywordSizeType == FIXED_CODE);
}
enum ParserKeywordSizeEnum ParserKeyword::getSizeType() const {
@@ -524,6 +561,9 @@ void set_dimensions( ParserItem& item,
return this->m_records.front().isDataRecord();
}
bool ParserKeyword::isCodeKeyword() const {
return (this->m_keywordSizeType == FIXED_CODE);
}
bool ParserKeyword::hasMatchRegex() const {
return !m_matchRegexString.empty();
@@ -588,26 +628,25 @@ void set_dimensions( ParserItem& item,
const std::string lhs = "keyword";
const std::string indent = " ";
ss << className() << "::" << className() << "( ) : ParserKeyword(\"" << m_name << "\") {" << '\n';
ss << className() << "::" << className() << "( ) : ParserKeyword(\"" << m_name << "\")" << '\n' << "{" << '\n';
{
const std::string sizeString(ParserKeywordSizeEnum2String(m_keywordSizeType));
ss << indent;
switch (m_keywordSizeType) {
case SLASH_TERMINATED:
ss << "setSizeType(" << sizeString << ");" << '\n';
break;
case UNKNOWN:
ss << "setSizeType(" << sizeString << ");" << '\n';
break;
case FIXED:
ss << "setFixedSize( (size_t) " << m_fixedSize << ");" << '\n';
break;
case OTHER_KEYWORD_IN_DECK:
ss << "setSizeType(" << sizeString << ");" << '\n';
ss << indent << "initSizeKeyword(\"" << keyword_size.keyword << "\",\"" << keyword_size.item << "\"," << keyword_size.shift << ");" << '\n';
if (m_isTableCollection)
ss << "setTableCollection( true );" << '\n';
break;
case SLASH_TERMINATED:
case FIXED_CODE:
case UNKNOWN:
ss << "setSizeType(" << sizeString << ");" << '\n';
break;
case FIXED:
ss << "setFixedSize( (size_t) " << m_fixedSize << ");" << '\n';
break;
case OTHER_KEYWORD_IN_DECK:
ss << "setSizeType(" << sizeString << ");" << '\n';
ss << indent << "initSizeKeyword(\"" << keyword_size.keyword << "\",\"" << keyword_size.item << "\"," << keyword_size.shift << ");" << '\n';
if (m_isTableCollection)
ss << indent << "setTableCollection( true );" << '\n';
break;
}
}
@@ -632,6 +671,9 @@ void set_dimensions( ParserItem& item,
if (hasMatchRegex())
ss << indent << "setMatchRegex(\"" << m_matchRegexString << "\");" << '\n';
if (this->m_keywordSizeType == FIXED_CODE)
ss << indent << "setCodeEnd(\"" << this->code_end << "\");" << '\n';
{
if (m_records.size() > 0 ) {
for( const auto& record : *this ) {
@@ -690,12 +732,15 @@ void set_dimensions( ParserItem& item,
return false;
if( m_name != rhs.m_name
|| this->code_end != rhs.code_end
|| m_matchRegexString != rhs.m_matchRegexString
|| m_keywordSizeType != rhs.m_keywordSizeType
|| isCodeKeyword() != rhs.isCodeKeyword()
|| isDataKeyword() != rhs.isDataKeyword()
|| m_isTableCollection != rhs.m_isTableCollection )
return false;
switch( m_keywordSizeType ) {
case FIXED:
if( m_fixedSize != rhs.m_fixedSize )

View File

@@ -77,18 +77,24 @@ inline bool even_quotes( const T& str ) {
}
RawRecord::RawRecord(const string_view& singleRecordString) :
m_sanitizedRecordString( singleRecordString ),
m_recordItems( splitSingleRecordString( m_sanitizedRecordString ) )
RawRecord::RawRecord(const string_view& singleRecordString, bool text) :
m_sanitizedRecordString( singleRecordString )
{
if( !even_quotes( singleRecordString ) )
throw std::invalid_argument(
"Input string is not a complete record string, "
"offending string: '" + singleRecordString + "'"
);
if (text)
this->m_recordItems.push_back(this->m_sanitizedRecordString);
else {
this->m_recordItems = splitSingleRecordString( m_sanitizedRecordString );
if( !even_quotes( singleRecordString ) )
throw std::invalid_argument("Input string is not a complete record string, "
"offending string: '" + singleRecordString + "'");
}
}
RawRecord::RawRecord(const string_view& singleRecordString) :
RawRecord(singleRecordString, false)
{}
void RawRecord::prepend( size_t count, string_view tok ) {
this->m_recordItems.insert( this->m_recordItems.begin(), count, tok );

View File

@@ -35,6 +35,7 @@ namespace Opm {
class RawRecord {
public:
RawRecord( const string_view&, bool text);
explicit RawRecord( const string_view&);
inline string_view pop_front();

View File

@@ -0,0 +1,218 @@
/*
Copyright 2013 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <opm/parser/eclipse/Parser/ParserKeyword.hpp>
#include <opm/parser/eclipse/RawDeck/RawConsts.hpp>
#include <opm/parser/eclipse/RawDeck/RawKeyword.hpp>
#include <opm/parser/eclipse/RawDeck/RawRecord.hpp>
#include <opm/parser/eclipse/Utility/String.hpp>
namespace Opm {
static const std::string emptystr = "";
RawKeyword::RawKeyword(const string_view& name, Raw::KeywordSizeEnum sizeType , const std::string& filename, size_t lineNR, bool slash_terminated) :
slash_terminated_records(slash_terminated),
m_partialRecordString( emptystr )
{
if (sizeType == Raw::SLASH_TERMINATED || sizeType == Raw::UNKNOWN) {
commonInit(name.string(),filename,lineNR);
m_sizeType = sizeType;
} else
throw std::invalid_argument("Error - invalid sizetype on input");
}
RawKeyword::RawKeyword(const string_view& name , const std::string& filename, size_t lineNR , size_t inputSize, bool slash_terminated, bool isTableCollection ) :
slash_terminated_records(slash_terminated)
{
commonInit(name.string(),filename,lineNR);
if (isTableCollection) {
m_sizeType = Raw::TABLE_COLLECTION;
m_numTables = inputSize;
} else {
m_sizeType = Raw::FIXED;
m_fixedSize = inputSize;
if (m_fixedSize == 0)
m_isFinished = true;
else
m_isFinished = false;
}
}
void RawKeyword::commonInit(const std::string& name , const std::string& filename, size_t lineNR) {
setKeywordName( name );
m_filename = filename;
m_lineNR = lineNR;
this->m_is_title = name == "TITLE";
}
const std::string& RawKeyword::getKeywordName() const {
return m_name;
}
size_t RawKeyword::size() const {
return m_records.size();
}
static inline bool isTerminator( const string_view& line ) {
return line.size() == 1 && line.back() == RawConsts::slash;
}
/// Important method, being repeatedly called. When a record is terminated,
/// it is added to the list of records, and a new record is started.
void RawKeyword::addRawRecordString(const string_view& partialRecordString) {
if( m_partialRecordString == emptystr )
m_partialRecordString = partialRecordString;
else
m_partialRecordString = { m_partialRecordString.begin(), partialRecordString.end() };
if( isTerminator( m_partialRecordString ) ) {
if (m_sizeType == Raw::TABLE_COLLECTION) {
m_currentNumTables += 1;
if (m_currentNumTables == m_numTables) {
m_isFinished = true;
m_partialRecordString = emptystr;
return;
}
} else if( m_sizeType == Raw::SLASH_TERMINATED) {
m_isFinished = true;
m_partialRecordString = emptystr;
return;
}
}
if( m_isFinished ) return;
if( this->is_title() ) {
string_view recstr = m_partialRecordString == ""
? "untitled"
: m_partialRecordString;
m_records.emplace_back( recstr );
m_partialRecordString = emptystr;
m_isFinished = true;
return;
}
if( RawRecord::isTerminatedRecordString( partialRecordString ) ) {
this->m_partialRecordString = string_view{ this->m_partialRecordString.begin(), this->m_partialRecordString.end() - 1 };
this->terminateRecord(false);
}
}
void RawKeyword::terminateRecord(bool text) {
this->m_records.emplace_back( this->m_partialRecordString, text );
m_partialRecordString = emptystr;
if( m_sizeType == Raw::FIXED && m_records.size() == m_fixedSize )
m_isFinished = true;
};
const RawRecord& RawKeyword::getFirstRecord() const {
return *m_records.begin();
}
bool RawKeyword::isKeywordPrefix(const string_view& line, std::string& keyword ) {
// make the keyword string ALL_UPPERCASE because Eclipse seems
// to be case-insensitive (although this is one of its
// undocumented features...)
keyword = uppercase( ParserKeyword::getDeckName( line ).string() );
return isValidKeyword( keyword );
}
bool RawKeyword::isValidKeyword(const std::string& keywordCandidate) {
return ParserKeyword::validDeckName(keywordCandidate);
}
void RawKeyword::setKeywordName(const std::string& name) {
m_name = boost::algorithm::trim_right_copy(name);
if (!isValidKeyword(m_name)) {
throw std::invalid_argument("Not a valid keyword:" + name);
} else if (m_name.size() > Opm::RawConsts::maxKeywordLength) {
throw std::invalid_argument("Too long keyword:" + name);
} else if (boost::algorithm::trim_left_copy(m_name) != m_name) {
throw std::invalid_argument("Illegal whitespace start of keyword:" + name);
}
}
bool RawKeyword::isPartialRecordStringEmpty() const {
return m_partialRecordString.size() == 0;
}
void RawKeyword::finalizeUnknownSize() {
if (m_sizeType == Raw::UNKNOWN)
m_isFinished = true;
else
throw std::invalid_argument("Fatal error finalizing keyword:" + m_name + " Only RawKeywords with UNKNOWN size can be explicitly finalized.");
}
bool RawKeyword::isFinished() const {
return m_isFinished;
}
const std::string& RawKeyword::getFilename() const {
return m_filename;
}
size_t RawKeyword::getLineNR() const {
return m_lineNR;
}
RawKeyword::const_iterator RawKeyword::begin() const {
return this->m_records.begin();
}
RawKeyword::const_iterator RawKeyword::end() const {
return this->m_records.end();
}
RawKeyword::iterator RawKeyword::begin() {
return this->m_records.begin();
}
RawKeyword::iterator RawKeyword::end() {
return this->m_records.end();
}
bool RawKeyword::is_title() const {
return this->m_is_title;
}
Raw::KeywordSizeEnum RawKeyword::getSizeType() const {
return m_sizeType;
}
bool RawKeyword::slashTerminatedRecords() const {
return this->slash_terminated_records;
}
}

View File

@@ -0,0 +1 @@
{"name" : "PYACTION", "sections" : ["SCHEDULE"], "code" : {"end" : "<<<"}}

View File

@@ -0,0 +1,3 @@
{"name" : "PYACTION",
"sections" : ["RUNSPEC", "GRID", "EDIT", "PROPS", "REGIONS", "SOLUTION", "SUMMARY", "SCHEDULE"],
"code" : {"end" : "<<<"}}

View File

@@ -566,8 +566,10 @@ set( keywords
900_OPM/O/OILDENT
900_OPM/P/PINTDIMS
900_OPM/P/PLYVMH
900_OPM/P/POLYMW
900_OPM/P/PLYMWINJ
900_OPM/P/POLYMW
900_OPM/P/PYACTION
900_OPM/P/PYINPUT
900_OPM/R/RHO
900_OPM/S/SKPRPOLY
900_OPM/S/SKPRWAT

79
tests/parser/PYACTION.cpp Normal file
View File

@@ -0,0 +1,79 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#define BOOST_TEST_MODULE PY_ACTION_TESTER
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <opm/parser/eclipse/Parser/Parser.hpp>
using namespace Opm;
BOOST_AUTO_TEST_CASE(ParsePYACTION) {
const std::string input_code = R"(from math import sin
import random
print("sin(0) = {}".format(sin(0)))
#---
if random.random() > 0.25:
print("Large outcome")
else:
print("Small result")
A = 100
B = A / 10
C = B * 20
)";
const std::string deck_string1 = R"(
SCHEDULE
PYACTION Her comes an ignored comment
)" + input_code + "<<<";
const std::string deck_string2 = R"(
SCHEDULE
PYACTION
)" + input_code;
const std::string deck_string3 = R"(
SCHEDULE
PYACTION -- Comment
)" + input_code + "<<<" + "\nGRID";
Parser parser;
{
auto deck = parser.parseString(deck_string1);
const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0);
BOOST_CHECK_EQUAL(parsed_code, input_code);
}
{
auto deck = parser.parseString(deck_string2);
const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0);
BOOST_CHECK_EQUAL(parsed_code, input_code);
}
{
auto deck = parser.parseString(deck_string3);
const auto& parsed_code = deck.getKeyword("PYACTION").getRecord(0).getItem("code").get<std::string>(0);
BOOST_CHECK_EQUAL(parsed_code, input_code);
BOOST_CHECK( deck.hasKeyword("GRID"));
}
}