blob: 1963da46c8119865eb33634d4e911591bb1454ae [file] [log] [blame]
/* Copyright 2015. Los Alamos National Security, LLC. This material was produced
* under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National
* Laboratory (LANL), which is operated by Los Alamos National Security, LLC
* for the U.S. Department of Energy. The U.S. Government has rights to use,
* reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS
* ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
* ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
* to produce derivative works, such modified software should be clearly marked,
* so as not to confuse it with the version available from LANL.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Under this license, it is required to include a reference to this work. We
* request that each derivative work contain a reference to LANL Copyright
* Disclosure C15076/LA-CC-15-054 so that this work's impact can be roughly
* measured.
*
* This is LANL Copyright Disclosure C15076/LA-CC-15-054
*/
/*
* PowerParser is a general purpose input file parser for software applications.
*
* Authors: Chuck Wingate XCP-2 caw@lanl.gov
* Robert Robey XCP-2 brobey@lanl.gov
*/
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <sstream>
#include <map>
#include <math.h>
#include "Variable.hh"
#include "Function.hh"
#include "Word.hh"
#include "Parser_math.hh"
#include "Cmd.hh"
#include "Whenthen.hh"
namespace PP
{
using std::cout;
using std::endl;
using std::string;
using std::deque;
using std::vector;
using std::stringstream;
using std::pair;
using std::ifstream;
using std::ios;
// ===========================================================================
// Default constructor.
// ===========================================================================
Whenthen::Whenthen()
{
processed = false;
seqdex = -1;
ever_flag = false;
}
// ===========================================================================
// Usual constructor.
// ===========================================================================
Whenthen::Whenthen(int &nwhen, Cmd &cmdi, bool &skipwhen,
bool &single_line_when, bool eflag,
stringstream &serr, int &ierr)
{
processed = false;
seqdex = -1;
ever_flag = eflag;
nwhen += 1;
skipwhen = true;
single_line_when = false;
int nwords = cmdi.get_nwords();
// &&&&&cw
//stringstream ssprint;
//cmdi.print_using_words(ssprint);
//cout << ssprint.str() << endl;
if (nwords < 7) {
cmdi.fatal_error(0, serr, ierr);
serr << "A when command line must have at least 7 words on it (the "
<< endl
<< "opening and closing parenthses each count as a word)"
<< endl;
serr << "This when command only has " << nwords << " words on it." << endl;
serr << "Expected something like (this has 7 words):" << endl;
serr << " when (time .gt. 5) then" << endl;
serr << "Or perhaps a single line when like (this has 9 words):" << endl;
serr << " when (time .gt. 5) shortmodcyc = 5" << endl;
ierr = 2;
return;
}
string p1 = cmdi.get_string(1);
if (p1 != "(") {
cmdi.fatal_error(1, serr, ierr);
serr << "Expected an open parentheses following the when keyword."
<< endl;
serr << "Instead found " << p1 << " following the when keyword."
<< endl;
serr << "The when command should be something like:" << endl;
serr << " when (time .gt. 5) then" << endl;
serr << "Or perhaps a single line when like:" << endl;
serr << " when (time .gt. 5) shortmodcyc = 5" << endl;
ierr = 2;
return;
}
for (int i=1; i<nwords-1; i++) {
string t1 = cmdi.get_string(i);
if (t1 == "then") {
cmdi.fatal_error(i, serr, ierr);
serr << "Found a then keyword embedded in the when command."
<< endl;
serr << "If a then keyword is present it must be the last "
"word on the line." << endl;
serr << "The when command should be something like:" << endl;
serr << " when (time .gt. 5) then" << endl;
serr << "Or perhaps a single line when like:" << endl;
serr << " when (time .gt. 5) shortmodcyc = 5" << endl;
ierr = 2;
return;
}
}
// Find the closing parenthesis
int close_paren_dex = -1;
for (int i=2; i<nwords; i++) {
string pi = cmdi.get_string(i);
if (pi == "then") break;
if (pi == ")") {
close_paren_dex = i;
break;
}
}
if (close_paren_dex == -1) {
cmdi.fatal_error(0, serr, ierr);
serr << "Expected a close parentheses following the condition."
<< endl;
serr << "Did not find a close parentheses." << endl;
serr << "The when command should be something like:" << endl;
serr << " when (time .gt. 5) then" << endl;
serr << "Or perhaps a single line when like:" << endl;
serr << " when (time .gt. 5) shortmodcyc = 5" << endl;
ierr = 2;
return;
}
int nw = close_paren_dex - 2;
if ((nw+1)%4 != 0) {
cmdi.fatal_error(0, serr, ierr);
serr << "Wrong number of words in the when...then condition."
<< endl;
serr << "The number of words in this condition is " << nw << endl;
serr << "The number of words + 1 should be a multiple of 4." << endl;
serr << "The condition should be something like:" << endl;
serr << " time .gt. 5" << endl;
serr << "This has 3 words and 3+1 is a multiple of 4." << endl;
serr << "Or the following is valid" << endl;
serr << " time .gt. 5 .and. ncycle .ge. 10" << endl;
serr << "This has 7 words and 7+1 is a multiple of 4." << endl;
ierr = 2;
return;
}
for (int i=2; i<close_paren_dex; i+=4) {
add_word(cmdi, i, varname);
add_word(cmdi, i+1, relation);
add_word(cmdi, i+2, value);
if (i+3 < close_paren_dex) add_word(cmdi, i+3, logop);
else add_word(cmdi, i+3, logop, "none");
satisfied.push_back("false");
}
// Check to make sure that the relation is valid.
for (int n=0; n<(int)varname.size(); n++) {
bool valid_relation = false;
if (relation[n].get_string() == ".hglt.") valid_relation = true;
if (relation[n].get_string() == ".hgle.") valid_relation = true;
if (relation[n].get_string() == ".hgeq.") valid_relation = true;
if (relation[n].get_string() == ".hgne.") valid_relation = true;
if (relation[n].get_string() == ".hggt.") valid_relation = true;
if (relation[n].get_string() == ".hgge.") valid_relation = true;
if (relation[n].get_string() == ".lt.") valid_relation = true;
if (relation[n].get_string() == ".le.") valid_relation = true;
if (relation[n].get_string() == ".eq.") valid_relation = true;
if (relation[n].get_string() == ".ne.") valid_relation = true;
if (relation[n].get_string() == ".gt.") valid_relation = true;
if (relation[n].get_string() == ".ge.") valid_relation = true;
if (!valid_relation) {
relation[n].fatal_error(serr, ierr);
serr << "Invalid when...then relation." << endl;
serr << "Expected .lt., .le., .eq., .ne., .gt., .ge." << endl;
serr << "Also could be .hglt., .hgle., .hgeq., .hgne., .hggt., .hgge." << endl;
serr << "Instead found relation: " << relation[n].get_string() << endl;
ierr = 2;
return;
}
}
// Set the has gotten to flags.
for (int n=0; n<(int)varname.size(); n++) {
bool hg = false;
if (relation[n].get_string() == ".hglt.") {
hg = true;
relation[n].set_value(".lt.");
}
else if (relation[n].get_string() == ".hgle.") {
hg = true;
relation[n].set_value(".le.");
}
else if (relation[n].get_string() == ".hgeq.") {
hg = true;
relation[n].set_value(".eq.");
}
else if (relation[n].get_string() == ".hgne.") {
hg = true;
relation[n].set_value(".ne.");
}
else if (relation[n].get_string() == ".hggt.") {
hg = true;
relation[n].set_value(".gt.");
}
else if (relation[n].get_string() == ".hgge.") {
hg = true;
relation[n].set_value(".ge.");
}
has_got.push_back(hg);
}
// Handle single line when...then
if (cmdi.get_string(nwords-1) != "then") {
single_line_when = true;
cmdi.delete_words(0, 5);
cmdi.reset_name_type();
skipwhen = false;
}
}
// ===========================================================================
// Add word.
// ===========================================================================
void Whenthen::add_word(Cmd &cmdi, int idex, deque<Word> &wq)
{
int ln = cmdi.get_line_number(idex);
int file_ln = cmdi.get_file_line_number(idex);
string fname = cmdi.get_filename(idex);
deque<string> *lines = cmdi.get_lines();
Word w(cmdi.get_string(idex), ln, file_ln, fname, lines);
wq.push_back(w);
}
void Whenthen::add_word(Cmd &cmdi, int idex, deque<Word> &wq, string sadd)
{
int ln = cmdi.get_line_number(idex);
int file_ln = cmdi.get_file_line_number(idex);
string fname = cmdi.get_filename(idex);
deque<string> *lines = cmdi.get_lines();
Word w(sadd, ln, file_ln, fname, lines);
wq.push_back(w);
}
// ===========================================================================
// Add a command to the deque of commands for this whenthen.
// ===========================================================================
void Whenthen::add_cmdf(Cmd &cmdi)
{
cmdsf.push_back(cmdi);
}
// ===========================================================================
// This is the check for when the condition is satisfied.
// ===========================================================================
void Whenthen::check_wt(vector<string> &code_varnames,
vector<string> &code_values,
vector<int> &vv_active,
int *wtci, stringstream &serr, int &ierr)
{
*wtci = 0;
if (processed) return;
Parser_math pmath;
deque<Word> wordsf;
bool skip_sat = false;
int num_sub_cond = (int)varname.size();
for (int n=0; n<num_sub_cond; n++) {
deque<Word> words;
if (satisfied[n] == "true") {
int ln = varname[n].get_line_number();
int file_ln = varname[n].get_file_line_number();
string fname = varname[n].get_filename();
deque<string> *lines = varname[n].get_lines();
Word w("true", ln, file_ln, fname, lines);
words.push_back(w);
}
else {
words.push_back(varname[n]);
words.push_back(relation[n]);
words.push_back(value[n]);
process_words(words, code_varnames, code_values, vv_active,
serr, ierr);
if (has_got[n]) {
if (words[0].get_bool(serr, ierr)) {
bool doit = true;
if (n > 0) {
if (logop[n-1].get_string() == ".andthen." && skip_sat) {
doit = false;
}
}
if (doit) satisfied[n] = "true";
}
else {
skip_sat = true;
}
}
}
wordsf.push_back(words[0]);
if (logop[n].get_string() == "none") break;
else wordsf.push_back(logop[n]);
}
process_words(wordsf, code_varnames, code_values, vv_active,
serr, ierr);
// The output value, wtci, defaults to false (0). If the condition
// is satisfied then the output is true (1).
if (wordsf[0].get_bool(serr, ierr)) {
*wtci = 1;
if (!ever_flag) processed = true;
return;
}
}
// ===========================================================================
// Given a deque of words, go through them evaluating relational and logical
// operators. The words should evaluate to one final word.
// ===========================================================================
void Whenthen::process_words(deque <Word> &words, vector<string> &code_varnames,
vector<string> &code_values,
vector<int> &vv_active,
stringstream &serr, int &ierr)
{
Parser_math pmath;
// Replace any code vars with their values.
int i2 = (int)words.size();
for (int i=0; i<i2; i++) {
for (int j=0; j<(int)code_varnames.size(); j++) {
if (words[i].get_string() == code_varnames[j]) {
int ln = words[i].get_line_number();
int file_ln = words[i].get_file_line_number();
string fname = words[i].get_filename();
deque<string> *lines = words[i].get_lines();
if (vv_active[j] == 0) {
Word wj("false", ln, file_ln, fname, lines);
replace_words(i, i+2, words, wj);
i2 -= 2;
break;
}
else {
Word wj(code_values[j], ln, file_ln, fname, lines);
words[i] = wj;
}
}
}
}
int i1 = 0;
i2 = (int)words.size() - 1;
for (int level=6; level>=0; level--) {
for (int i=i1; i<=i2; i+=1) {
if (words[i].is_operator(level)) {
int ln = words[i].get_line_number();
int file_ln = words[i].get_file_line_number();
string fname = words[i].get_filename();
deque<string> *lines = words[i].get_lines();
Word w("", ln, file_ln, fname, lines);
string op_type = words[i].get_op_type();
if (op_type == "relational") {
pmath.do_op_relational(i-1, i, i+1, words, w, serr, ierr);
}
if (op_type == "logical" && level == 2) // .not. is unary
pmath.do_op_not(i, i+1, words, w, serr, ierr);
if (op_type == "logical" && level != 2)
pmath.do_op_logical(i-1, i, i+1, words, w, serr, ierr);
// level 2, .not., is unary and is handled differently.
if (level == 2) {
replace_words(i, i+1, words, w);
i2 -= 1;
}
else {
replace_words(i-1, i+1, words, w);
i2 -= 2;
i -= 1;
}
continue;
}
}
}
// The condition has to evaluate to a single boolean value.
if ((int)words.size() != 1) {
words[0].fatal_error(serr, ierr);
serr << "When...then condition did not evaluate to a single boolean value."
<< endl;
serr << "Fix the when...then condition" << endl;
ierr = 2;
}
}
// ===========================================================================
// ===========================================================================
void Whenthen::get_char_array_size(int *ca_size)
{
string sc;
get_char_array(sc);
(*ca_size) = (int)sc.size();
}
// ===========================================================================
// ===========================================================================
void Whenthen::get_char_array(string &sc)
{
for (int n=0; n<(int)varname.size(); n++) {
sc += varname[n].get_string();
sc += relation[n].get_string();
sc += value[n].get_string();
sc += logop[n].get_string();
if (has_got[n]) sc += "hasgot";
}
for (int n=0; n<(int)cmdsf.size(); n++) {
int nw = cmdsf[n].get_nwords();
for (int i=0; i<nw; i++) {
sc += cmdsf[n].get_string(i);
}
}
}
// ===========================================================================
// ===========================================================================
void Whenthen::get_satsize(int *sat_size)
{
(*sat_size) = (int)satisfied.size();
}
// ===========================================================================
// Get and Set the satisfied flags.
// ===========================================================================
void Whenthen::getsat(int *sat)
{
for (int i=0; i<(int)satisfied.size(); i++) {
if (satisfied[i] == "true") sat[i] = 1;
if (satisfied[i] == "false") sat[i] = 0;
}
}
void Whenthen::setsat(int *sat)
{
for (int i=0; i<(int)satisfied.size(); i++) {
if (sat[i] == 1) satisfied[i] = "true";
if (sat[i] == 0) satisfied[i] = "false";
}
}
// ===========================================================================
// Get and Set the processed flag for a whenthen.
// ===========================================================================
void Whenthen::getprocessed(int *wtp)
{
*wtp = 0;
if (processed) *wtp = 1;
}
void Whenthen::setprocessed(int wtp)
{
processed = false;
if (wtp == 1) processed = true;
}
// ===========================================================================
// Get and Set the sequence index.
// ===========================================================================
void Whenthen::getseq(int *wtseq)
{
*wtseq = seqdex;
}
void Whenthen::setseq(int wtseq)
{
seqdex = wtseq;
}
// ===========================================================================
// List the final commands deque for this whenthen to a stringstream.
// This is done to let the user know what the final commands are. It is
// also useful for debugging.
// ===========================================================================
void Whenthen::list_cmdsf_ss(stringstream &ssc)
{
for (int i=0; i<(int)cmdsf.size(); i++) {
ssc << " ";
cmdsf[i].print_using_words_fm(ssc);
ssc << endl;
}
}
// ===========================================================================
// List the condition for this whenthen to a stringstream.
// This is done to let the user indentify this whenthen. It is
// also useful for debugging.
// ===========================================================================
void Whenthen::list_condition(string offset1, string offset2,
stringstream &ssc)
{
for (int n=0; n<(int)varname.size(); n++) {
string relstr = relation[n].get_string();
string rstr = relstr;
if (has_got[n]) {
if (relstr == ".lt.") rstr = ".hglt.";
if (relstr == ".le.") rstr = ".hgle.";
if (relstr == ".eq.") rstr = ".hgeq.";
if (relstr == ".ne.") rstr = ".hgne.";
if (relstr == ".gt.") rstr = ".hggt.";
if (relstr == ".ge.") rstr = ".hgge.";
}
relstr = rstr;
string offset = offset1;
if (n > 0) offset = offset2;
ssc << offset << varname[n].get_string() << " "
<< relstr << " " << value[n].get_string();
if (logop[n].get_string() == "none") break;
ssc << " " << logop[n].get_string();
ssc << endl;
}
}
// ===========================================================================
// Delete words i1 through i2 inclusive from the deque.
// ===========================================================================
void Whenthen::delete_words(int i1, int i2, deque <Word> &words)
{
deque<Word>::iterator p = words.begin();
words.erase(p + i1, p + i2 + 1);
}
// ===========================================================================
// Replace words i1 through i2 inclusive with word w.
// ===========================================================================
void Whenthen::replace_words(int i1, int i2, deque <Word> &words, Word &w)
{
delete_words(i1, i2, words);
deque<Word>::iterator p = words.begin();
words.insert(p + i1, w);
}
} // End of the PP namespace