Merge pull request #935 from joakim-hove/parse-python
Started to parse Python code
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ namespace Opm {
|
||||
SLASH_TERMINATED = 0,
|
||||
FIXED = 1,
|
||||
OTHER_KEYWORD_IN_DECK = 2,
|
||||
UNKNOWN=3
|
||||
UNKNOWN = 3,
|
||||
FIXED_CODE = 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
/*
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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()");
|
||||
}
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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();
|
||||
|
||||
218
src/opm/parser/eclipse/RawDeck/RawKeyword.cpp
Normal file
218
src/opm/parser/eclipse/RawDeck/RawKeyword.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION
Normal file
1
src/opm/parser/eclipse/share/keywords/900_OPM/P/PYACTION
Normal file
@@ -0,0 +1 @@
|
||||
{"name" : "PYACTION", "sections" : ["SCHEDULE"], "code" : {"end" : "<<<"}}
|
||||
3
src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT
Normal file
3
src/opm/parser/eclipse/share/keywords/900_OPM/P/PYINPUT
Normal file
@@ -0,0 +1,3 @@
|
||||
{"name" : "PYACTION",
|
||||
"sections" : ["RUNSPEC", "GRID", "EDIT", "PROPS", "REGIONS", "SOLUTION", "SUMMARY", "SCHEDULE"],
|
||||
"code" : {"end" : "<<<"}}
|
||||
@@ -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
79
tests/parser/PYACTION.cpp
Normal 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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user