You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
935 lines
32 KiB
935 lines
32 KiB
// Copyright(C) 1999-2023 National Technology & Engineering Solutions
|
|
// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
|
// NTESS, the U.S. Government retains certain rights in this software.
|
|
//
|
|
// See packages/seacas/LICENSE for details
|
|
|
|
#include "apr_scanner.h" // for Scanner
|
|
#include "apr_symrec.h"
|
|
#include "apr_util.h"
|
|
#include "aprepro.h" // for Aprepro, symrec, file_rec, etc
|
|
#include "aprepro_parser.h" // for Parser, Parser::token, etc
|
|
#include "terminal_color.h"
|
|
#include <cstdlib> // for exit, EXIT_SUCCESS, etc
|
|
#include <cstring> // for strcmp
|
|
#include <fstream> // for operator<<, basic_ostream, etc
|
|
#include <iomanip> // for operator<<, setw, etc
|
|
#include <iostream> // for left, cerr, cout, streampos
|
|
#include <stack> // for stack
|
|
#include <stdexcept>
|
|
#include <stdio.h>
|
|
#include <string> // for string, operator==, etc
|
|
#include <unistd.h>
|
|
#include <vector> // for allocator, vector
|
|
|
|
#define HASHSIZE 5939
|
|
|
|
#define USE_ROBIN_MAP
|
|
#if defined USE_ROBIN_MAP
|
|
#include <robin_map.h>
|
|
#else
|
|
#include <unordered_map>
|
|
#endif
|
|
|
|
namespace {
|
|
const std::string version_string{"6.12 (2023/05/03)"};
|
|
|
|
void output_copyright();
|
|
|
|
std::string get_value(const std::string &option, const std::string &optional_value)
|
|
{
|
|
size_t index = option.find_first_of('=');
|
|
std::string value;
|
|
|
|
if (index != std::string::npos) {
|
|
value = option.substr(index + 1);
|
|
}
|
|
else {
|
|
value = optional_value;
|
|
}
|
|
return value;
|
|
}
|
|
} // namespace
|
|
|
|
namespace SEAMS {
|
|
struct Symtable
|
|
{
|
|
Symtable() = default;
|
|
~Symtable()
|
|
{
|
|
for (const auto &sym : sym_table) {
|
|
const auto &ptr = sym.second;
|
|
delete ptr;
|
|
}
|
|
}
|
|
|
|
void add(const std::string &name, SEAMS::symrec *ptr) { sym_table[name] = ptr; }
|
|
void erase(const std::string &name) { sym_table.erase(name); }
|
|
SEAMS::symrec *getsym(const char *sym_name)
|
|
{
|
|
auto ptr = sym_table.find(sym_name);
|
|
if (ptr != sym_table.end()) {
|
|
return ptr->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#if defined USE_ROBIN_MAP
|
|
tsl::robin_pg_map<std::string, SEAMS::symrec *> sym_table{HASHSIZE};
|
|
const tsl::robin_pg_map<std::string, SEAMS::symrec *> &get() { return sym_table; }
|
|
#else
|
|
std::unordered_map<std::string, SEAMS::symrec *> sym_table{HASHSIZE};
|
|
const std::unordered_map<std::string, SEAMS::symrec *> &get() { return sym_table; }
|
|
#endif
|
|
};
|
|
|
|
Aprepro *aprepro = nullptr; // A global for use in the library. Clean this up...
|
|
bool echo = true;
|
|
|
|
Aprepro::Aprepro() : sym_table(new Symtable())
|
|
{
|
|
ap_file_list.push(file_rec());
|
|
init_table("$");
|
|
aprepro = this;
|
|
}
|
|
|
|
Aprepro::~Aprepro()
|
|
{
|
|
if (!outputStream.empty()) {
|
|
outputStream.top()->flush();
|
|
}
|
|
|
|
// May need to delete this if set via --info=filename command.
|
|
// May need a flag to determine this...
|
|
infoStream->flush();
|
|
if (closeInfo) {
|
|
delete infoStream;
|
|
}
|
|
|
|
if ((stringScanner != nullptr) && stringScanner != lexer) {
|
|
delete stringScanner;
|
|
}
|
|
|
|
delete lexer;
|
|
|
|
aprepro = nullptr;
|
|
|
|
for (auto &arr_mem : array_allocations) {
|
|
delete arr_mem;
|
|
}
|
|
array_allocations.clear();
|
|
|
|
cleanup_memory();
|
|
}
|
|
|
|
std::string Aprepro::version() { return version_string; }
|
|
|
|
std::string Aprepro::long_version() const
|
|
{
|
|
auto comment = getsym("_C_")->value.svar;
|
|
return comment + " Algebraic Preprocessor (Aprepro) version " + version();
|
|
}
|
|
|
|
void Aprepro::clear_results()
|
|
{
|
|
parsingResults.str("");
|
|
parsingResults.clear();
|
|
}
|
|
|
|
bool Aprepro::parse_stream(std::istream &in, const std::string &in_name)
|
|
{
|
|
std::istream *in_cpy = ∈
|
|
ap_file_list.top().name = in_name;
|
|
|
|
auto scanner = new Scanner(*this, in_cpy, &parsingResults);
|
|
this->lexer = scanner;
|
|
|
|
if (!ap_options.include_file.empty()) {
|
|
stateImmutable = true;
|
|
echo = false;
|
|
scanner->add_include_file(ap_options.include_file, true);
|
|
}
|
|
|
|
Parser parser(*this);
|
|
parser.set_debug_level(ap_options.trace_parsing);
|
|
return (parser.parse() == 0);
|
|
}
|
|
|
|
bool Aprepro::parse_file(const std::string &filename)
|
|
{
|
|
std::ifstream in(filename);
|
|
if (!in.good()) {
|
|
return false;
|
|
}
|
|
return parse_stream(in, filename);
|
|
}
|
|
|
|
bool Aprepro::parse_string(const std::string &input, const std::string &sname)
|
|
{
|
|
std::istringstream iss(input);
|
|
return parse_stream(iss, sname);
|
|
}
|
|
|
|
bool Aprepro::parse_strings(const std::vector<std::string> &input, const std::string &sname)
|
|
{
|
|
std::stringstream iss;
|
|
for (const auto &elem : input) {
|
|
iss << elem << '\n';
|
|
}
|
|
return parse_stream(iss, sname);
|
|
}
|
|
|
|
bool Aprepro::parse_string_interactive(const std::string &input)
|
|
{
|
|
stringInteractive = true;
|
|
|
|
if (ap_file_list.size() == 1) {
|
|
ap_file_list.top().name = "interactive_input";
|
|
}
|
|
if (stringScanner == nullptr) {
|
|
stringScanner = new Scanner(*this, &stringInput, &parsingResults);
|
|
}
|
|
|
|
if (!ap_options.include_file.empty()) {
|
|
stateImmutable = true;
|
|
echo = false;
|
|
stringScanner->add_include_file(ap_options.include_file, true);
|
|
}
|
|
|
|
this->lexer = stringScanner;
|
|
|
|
stringInput.str(input);
|
|
stringInput.clear();
|
|
|
|
Parser parser(*this);
|
|
parser.set_debug_level(ap_options.trace_parsing);
|
|
bool result = parser.parse() == 0;
|
|
|
|
stringInteractive = false;
|
|
return result;
|
|
}
|
|
|
|
void Aprepro::error(const std::string &msg, bool line_info, bool prefix) const
|
|
{
|
|
bool colorize = (errorStream == &std::cerr) && isatty(fileno(stderr));
|
|
std::stringstream ss;
|
|
if (prefix) {
|
|
if (colorize) {
|
|
(*errorStream) << trmclr::red;
|
|
}
|
|
(*errorStream) << "Aprepro: ERROR: ";
|
|
}
|
|
|
|
ss << msg;
|
|
if (prefix && colorize) {
|
|
ss << trmclr::normal;
|
|
}
|
|
|
|
if (line_info) {
|
|
ss << " (" << ap_file_list.top().name << ", line " << ap_file_list.top().lineno << ")";
|
|
}
|
|
ss << "\n";
|
|
|
|
// Send it to the user defined stream
|
|
(*errorStream) << ss.str();
|
|
parseErrorCount++;
|
|
}
|
|
|
|
void Aprepro::warning(const std::string &msg, bool line_info, bool prefix) const
|
|
{
|
|
if (!ap_options.warning_msg) {
|
|
return;
|
|
}
|
|
|
|
bool colorize = (warningStream == &std::cerr) && isatty(fileno(stderr));
|
|
std::stringstream ss;
|
|
if (prefix) {
|
|
if (colorize) {
|
|
(*warningStream) << trmclr::yellow;
|
|
}
|
|
(*warningStream) << "Aprepro: WARNING: ";
|
|
}
|
|
|
|
ss << msg;
|
|
|
|
if (prefix && colorize) {
|
|
ss << trmclr::normal;
|
|
}
|
|
|
|
if (line_info) {
|
|
ss << " (" << ap_file_list.top().name << ", line " << ap_file_list.top().lineno << ")";
|
|
}
|
|
ss << "\n";
|
|
|
|
// Send it to the user defined stream
|
|
(*warningStream) << ss.str();
|
|
parseWarningCount++;
|
|
}
|
|
|
|
void Aprepro::info(const std::string &msg, bool line_info, bool prefix) const
|
|
{
|
|
if (!ap_options.info_msg) {
|
|
return;
|
|
}
|
|
|
|
bool colorize = (infoStream == &std::cout) && isatty(fileno(stdout));
|
|
std::stringstream ss;
|
|
if (prefix) {
|
|
if (colorize) {
|
|
(*infoStream) << trmclr::blue;
|
|
}
|
|
(*infoStream) << "Aprepro: INFO: ";
|
|
}
|
|
ss << msg;
|
|
|
|
if (prefix && colorize) {
|
|
ss << trmclr::normal;
|
|
}
|
|
|
|
if (line_info) {
|
|
ss << " (" << ap_file_list.top().name << ", line " << ap_file_list.top().lineno << ")";
|
|
}
|
|
ss << "\n";
|
|
|
|
// Send it to the user defined stream
|
|
(*infoStream) << ss.str();
|
|
}
|
|
|
|
void Aprepro::set_error_streams(std::ostream *c_error, std::ostream *c_warning,
|
|
std::ostream *c_info, bool close_error, bool close_warning,
|
|
bool close_info)
|
|
{
|
|
if (c_error != nullptr) {
|
|
errorStream = c_error;
|
|
closeError = close_error;
|
|
}
|
|
if (c_warning != nullptr) {
|
|
warningStream = c_warning;
|
|
closeWarning = close_warning;
|
|
}
|
|
if (c_info != nullptr) {
|
|
infoStream = c_info;
|
|
closeInfo = close_info;
|
|
}
|
|
}
|
|
|
|
void Aprepro::set_error_streams(std::ostream *c_error, std::ostream *c_warning,
|
|
std::ostream *c_info)
|
|
{
|
|
set_error_streams(c_error, c_warning, c_info, false, false, false);
|
|
}
|
|
|
|
/* Two methods for opening files:
|
|
|
|
In OPEN_FILE, the file must exist or else the code will exit in
|
|
batch mode or return null pointer if interactive.
|
|
|
|
In CHECK_OPEN_FILE, it is OK if the file does not exist. A Null
|
|
'pointer' will be returned.
|
|
*/
|
|
std::fstream *Aprepro::open_file(const std::string &file, const char *mode)
|
|
{
|
|
std::ios::openmode smode = std::ios::in;
|
|
if (mode[0] == 'w') {
|
|
smode = std::ios::out;
|
|
}
|
|
|
|
/* See if file exists in current directory (or as specified) */
|
|
auto pointer = new std::fstream(file, smode);
|
|
if ((pointer->bad() || !pointer->good()) && !ap_options.include_path.empty()) {
|
|
/* If there is an include path specified, try opening file there */
|
|
std::string file_path(ap_options.include_path);
|
|
file_path += "/";
|
|
file_path += file;
|
|
delete pointer;
|
|
pointer = new std::fstream(file_path, smode);
|
|
}
|
|
|
|
/* If pointer still null, print error message */
|
|
if (pointer->fail() || pointer->bad() || !pointer->good()) {
|
|
std::string err = "Can't open '" + file + "'. " + strerror(errno);
|
|
error(err, false);
|
|
delete pointer;
|
|
pointer = nullptr;
|
|
if (!stringInteractive) {
|
|
throw std::runtime_error(err);
|
|
}
|
|
}
|
|
|
|
return pointer;
|
|
}
|
|
|
|
std::fstream *Aprepro::check_open_file(const std::string &file, const char *mode)
|
|
{
|
|
std::ios::openmode smode = std::ios::in;
|
|
if (mode[0] == 'w') {
|
|
smode = std::ios::out;
|
|
}
|
|
|
|
auto pointer = new std::fstream(file, smode);
|
|
|
|
if ((pointer->bad() || !pointer->good()) && !ap_options.include_path.empty()) {
|
|
/* If there is an include path specified, try opening file there */
|
|
std::string file_path(ap_options.include_path);
|
|
file_path += "/";
|
|
file_path += file;
|
|
delete pointer;
|
|
pointer = new std::fstream(file_path, smode);
|
|
}
|
|
return pointer;
|
|
}
|
|
|
|
symrec *Aprepro::putsym(const std::string &sym_name, SYMBOL_TYPE sym_type, bool is_internal)
|
|
{
|
|
int parser_type = 0;
|
|
bool is_function = false;
|
|
switch (sym_type) {
|
|
case SYMBOL_TYPE::VARIABLE: parser_type = Parser::token::VAR; break;
|
|
case SYMBOL_TYPE::STRING_VARIABLE: parser_type = Parser::token::SVAR; break;
|
|
case SYMBOL_TYPE::ARRAY_VARIABLE: parser_type = Parser::token::AVAR; break;
|
|
case SYMBOL_TYPE::IMMUTABLE_VARIABLE: parser_type = Parser::token::IMMVAR; break;
|
|
case SYMBOL_TYPE::IMMUTABLE_STRING_VARIABLE: parser_type = Parser::token::IMMSVAR; break;
|
|
case SYMBOL_TYPE::UNDEFINED_VARIABLE: parser_type = Parser::token::UNDVAR; break;
|
|
case SYMBOL_TYPE::FUNCTION:
|
|
parser_type = Parser::token::FNCT;
|
|
is_function = true;
|
|
break;
|
|
case SYMBOL_TYPE::STRING_FUNCTION:
|
|
parser_type = Parser::token::SFNCT;
|
|
is_function = true;
|
|
break;
|
|
case SYMBOL_TYPE::ARRAY_FUNCTION:
|
|
parser_type = Parser::token::AFNCT;
|
|
is_function = true;
|
|
break;
|
|
}
|
|
|
|
// If the type is a function type, it can be overloaded as long as
|
|
// it returns the same type which means that the "parser_type" is
|
|
// the same. If we have a function, see if it has already been
|
|
// defined and if so, check that the parser_type matches and then
|
|
// retrn that pointer instead of creating a new symrec.
|
|
|
|
if (is_function) {
|
|
symrec *ptr = getsym(sym_name);
|
|
if (ptr != nullptr) {
|
|
if (ptr->type != parser_type) {
|
|
std::string err = "Overloaded function '" + sym_name + "' does not return same type";
|
|
error(err, false);
|
|
throw std::runtime_error(err);
|
|
}
|
|
// Function with this name already exists; return that
|
|
// pointer.
|
|
// Note that the info and syntax fields will contain the
|
|
// latest values, not the firstt...
|
|
return ptr;
|
|
}
|
|
}
|
|
else {
|
|
symrec *ptr = getsym(sym_name);
|
|
if (ptr != nullptr) {
|
|
std::string err = "Internal Error: Variable '" + sym_name + "' is already defined.";
|
|
error(err, false);
|
|
throw std::runtime_error(err);
|
|
}
|
|
}
|
|
|
|
auto ptr = new symrec(sym_name, parser_type, is_internal);
|
|
sym_table->add(sym_name, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
int Aprepro::set_option(const std::string &option, const std::string &optional_value)
|
|
{
|
|
// Option should be of the form "--option" or "-O"
|
|
// I.e., double dash for long option, single dash for short.
|
|
// Option can be followed by "=value" if the option requires
|
|
// a value. The value will either be part of 'option' if it contains
|
|
// an '=', or it will be in 'optional_value'. if 'optional_value' used,
|
|
// return 1; else return 0;
|
|
|
|
// Some options (--include)
|
|
int ret_value = 0;
|
|
|
|
if (option == "--debug" || option == "-d") {
|
|
ap_options.debugging = true;
|
|
}
|
|
if (option == "--dumpvars" || option == "-D") {
|
|
ap_options.dumpvars = true;
|
|
}
|
|
else if (option == "--dumpvars_json" || option == "-J") {
|
|
ap_options.dumpvars_json = true;
|
|
}
|
|
else if (option == "--version" || option == "-v") {
|
|
std::cerr << "Algebraic Preprocessor (Aprepro) version " << version() << "\n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else if (option == "--nowarning" || option == "-W") {
|
|
ap_options.warning_msg = false;
|
|
}
|
|
else if (option == "--copyright" || option == "-C") {
|
|
output_copyright();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else if (option == "--message" || option == "-M") {
|
|
ap_options.info_msg = true;
|
|
}
|
|
else if (option == "--immutable" || option == "-X") {
|
|
ap_options.immutable = true;
|
|
stateImmutable = true;
|
|
}
|
|
else if (option == "--errors_fatal" || option == "-f") {
|
|
ap_options.errors_fatal = true;
|
|
}
|
|
else if (option == "--errors_and_warnings_fatal" || option == "-F") {
|
|
ap_options.errors_and_warnings_fatal = true;
|
|
ap_options.errors_fatal = true;
|
|
}
|
|
else if (option == "--require_defined" || option == "-R") {
|
|
ap_options.require_defined = true;
|
|
}
|
|
else if (option == "--trace" || option == "-t") {
|
|
ap_options.trace_parsing = true;
|
|
}
|
|
else if (option == "--interactive" || option == "-i") {
|
|
ap_options.interactive = true;
|
|
}
|
|
else if (option == "--one_based_index" || option == "-1") {
|
|
ap_options.one_based_index = true;
|
|
}
|
|
else if (option == "--exit_on" || option == "-e") {
|
|
ap_options.end_on_exit = true;
|
|
}
|
|
else if (option.find("--info") != std::string::npos) {
|
|
std::string value = get_value(option, optional_value);
|
|
ret_value = value == optional_value ? 1 : 0;
|
|
|
|
if (!value.empty()) {
|
|
auto do_info = open_file(value, "w");
|
|
if (do_info != nullptr) {
|
|
set_error_streams(nullptr, nullptr, do_info, false, false, true);
|
|
}
|
|
}
|
|
}
|
|
else if (option.find("--include") != std::string::npos || (option[1] == 'I')) {
|
|
std::string value = get_value(option, optional_value);
|
|
ret_value = value == optional_value ? 1 : 0;
|
|
|
|
if (is_directory(value)) {
|
|
ap_options.include_path = value;
|
|
}
|
|
else {
|
|
ap_options.include_file = value;
|
|
}
|
|
}
|
|
else if (option == "--keep_history" || option == "-k") {
|
|
ap_options.keep_history = true;
|
|
}
|
|
|
|
else if (option.find("--comment") != std::string::npos || (option[1] == 'c')) {
|
|
std::string comment;
|
|
// In short version, do not require equal sign (-c# -c// )
|
|
if (option[1] == 'c' && option.length() > 2 && option[2] != '=') {
|
|
comment = option.substr(2);
|
|
}
|
|
else {
|
|
comment = get_value(option, optional_value);
|
|
ret_value = comment == optional_value ? 1 : 0;
|
|
}
|
|
symrec *ptr = getsym("_C_");
|
|
if (ptr != nullptr) {
|
|
ptr->value.svar = comment;
|
|
}
|
|
}
|
|
|
|
else if (option == "--help" || option == "-h") {
|
|
std::cerr
|
|
<< "\nAprepro version " << version() << "\n"
|
|
<< "\nUsage: aprepro [options] [-I path] [-c char] [var=val] [filein] [fileout]\n"
|
|
<< " --debug or -d: Dump all variables, debug loops/if/endif and keep temporary files\n"
|
|
<< " --dumpvars or -D: Dump all variables at end of run \n"
|
|
<< " --dumpvars_json or -J: Dump all variables at end of run in json format\n"
|
|
<< " --version or -v: Print version number to stderr \n"
|
|
<< " --immutable or -X: All variables are immutable--cannot be modified\n"
|
|
<< " --errors_fatal or -f: Exit program with nonzero status if errors are "
|
|
"encountered\n"
|
|
<< " --errors_and_warnings_fatal or -F: Exit program with nonzero status if "
|
|
"warnings are encountered\n"
|
|
<< "--require_defined or -R: Treat undefined variable warnings as fatal\n"
|
|
<< "--one_based_index or -1: Array indexing is one-based (default = zero-based)\n"
|
|
<< " --interactive or -i: Interactive use, no buffering \n"
|
|
<< " --include=P or -I=P: Include file or include path \n"
|
|
<< " : If P is path, then optionally prepended to all include "
|
|
"filenames\n"
|
|
<< " : If P is file, then processed before processing input file\n"
|
|
<< " : variables defined in P will be immutable.\n"
|
|
<< " --exit_on or -e: End when 'Exit|EXIT|exit' entered \n"
|
|
<< " --help or -h: Print this list \n"
|
|
<< " --message or -M: Print INFO messages \n"
|
|
<< " --info=file: Output INFO messages (e.g. DUMP() output) to file.\n"
|
|
<< " --nowarning or -W: Do not print WARN messages \n"
|
|
<< " --comment=char or -c=char: Change comment character to 'char' \n"
|
|
<< " --copyright or -C: Print copyright message \n"
|
|
<< " --keep_history or -k: Keep a history of aprepro substitutions.\n"
|
|
<< " (not for general interactive use) \n"
|
|
<< " --quiet or -q: Do not print the header output line \n"
|
|
<< " var=val: Assign value 'val' to variable 'var' \n"
|
|
<< " Use var=\\\"sval\\\" for a string variable. 'var' will be "
|
|
"immutable.\n\n"
|
|
<< "\tUnits Systems: si, cgs, cgs-ev, shock, swap, ft-lbf-s, ft-lbm-s, in-lbf-s\n"
|
|
<< "\tEnter {DUMP()} for list of user-defined variables\n"
|
|
<< "\tEnter {DUMP_FUNC()} for list of functions recognized by aprepro\n"
|
|
<< "\tEnter {DUMP_PREVAR()} for list of predefined variables in aprepro\n\n"
|
|
<< "\tDocumentation: "
|
|
"https://sandialabs.github.io/seacas-docs/sphinx/html/index.html#aprepro\n\n"
|
|
<< "\t->->-> Send email to gdsjaar@sandia.gov for aprepro support.\n\n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
return ret_value;
|
|
}
|
|
|
|
array *Aprepro::make_array(int r, int c)
|
|
{
|
|
auto ptr = new array(r, c);
|
|
array_allocations.push_back(ptr);
|
|
return ptr;
|
|
}
|
|
|
|
array *Aprepro::make_array(const array &from)
|
|
{
|
|
auto ptr = new array(from);
|
|
array_allocations.push_back(ptr);
|
|
return ptr;
|
|
}
|
|
|
|
void Aprepro::redefine_array(array *array)
|
|
{
|
|
// This data pointer from an array is being redefined. Remove it
|
|
// from `array_allocations` to avoid double delete.
|
|
array_allocations.erase(std::remove(array_allocations.begin(), array_allocations.end(), array),
|
|
array_allocations.end());
|
|
delete array;
|
|
}
|
|
|
|
void Aprepro::add_variable(const std::string &sym_name, const std::string &sym_value,
|
|
bool immutable, bool internal)
|
|
{
|
|
if (check_valid_var(sym_name.c_str())) {
|
|
SYMBOL_TYPE type =
|
|
immutable ? SYMBOL_TYPE::IMMUTABLE_STRING_VARIABLE : SYMBOL_TYPE::STRING_VARIABLE;
|
|
symrec *var = getsym(sym_name);
|
|
if (var == nullptr) {
|
|
var = putsym(sym_name, type, internal);
|
|
}
|
|
else {
|
|
var->type = immutable ? Parser::token::IMMSVAR : Parser::token::SVAR;
|
|
}
|
|
var->value.svar = sym_value;
|
|
}
|
|
else {
|
|
warning("Invalid variable name syntax '" + sym_name + "'. Variable not defined.\n", false);
|
|
}
|
|
}
|
|
|
|
void Aprepro::add_variable(const std::string &sym_name, double sym_value, bool immutable,
|
|
bool internal)
|
|
{
|
|
if (check_valid_var(sym_name.c_str())) {
|
|
SYMBOL_TYPE type = immutable ? SYMBOL_TYPE::IMMUTABLE_VARIABLE : SYMBOL_TYPE::VARIABLE;
|
|
symrec *var = getsym(sym_name);
|
|
if (var == nullptr) {
|
|
var = putsym(sym_name, type, internal);
|
|
}
|
|
else {
|
|
var->type = immutable ? Parser::token::IMMVAR : Parser::token::VAR;
|
|
}
|
|
var->value.var = sym_value;
|
|
}
|
|
else {
|
|
warning("Invalid variable name syntax '" + sym_name + "'. Variable not defined.\n", false);
|
|
}
|
|
}
|
|
|
|
void Aprepro::add_variable(const std::string &sym_name, array *value)
|
|
{
|
|
if (check_valid_var(sym_name.c_str())) {
|
|
SYMBOL_TYPE type = SYMBOL_TYPE::ARRAY_VARIABLE;
|
|
symrec *var = getsym(sym_name);
|
|
if (var == nullptr) {
|
|
var = putsym(sym_name, type, false);
|
|
}
|
|
else {
|
|
var->type = Parser::token::AVAR;
|
|
}
|
|
var->value.avar = value;
|
|
}
|
|
else {
|
|
warning("Invalid variable name syntax '" + sym_name + "'. Variable not defined.\n", false);
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> Aprepro::get_variable_names(bool doInternal)
|
|
{
|
|
std::vector<std::string> names;
|
|
|
|
for (const auto &sym : sym_table->get()) {
|
|
const auto &ptr = sym.second;
|
|
if (ptr->isInternal != doInternal) {
|
|
continue;
|
|
}
|
|
|
|
switch (ptr->type) {
|
|
case Parser::token::VAR:
|
|
case Parser::token::IMMVAR:
|
|
case Parser::token::SVAR:
|
|
case Parser::token::IMMSVAR:
|
|
case Parser::token::AVAR:
|
|
// Add to our vector
|
|
names.push_back(ptr->name);
|
|
break;
|
|
|
|
default:
|
|
// Do nothing
|
|
break;
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
void Aprepro::remove_variable(const std::string &sym_name)
|
|
{
|
|
symrec *ptr = getsym(sym_name);
|
|
bool is_valid_variable =
|
|
(ptr != nullptr) && (!ptr->isInternal) &&
|
|
((ptr->type == Parser::token::VAR) || (ptr->type == Parser::token::SVAR) ||
|
|
(ptr->type == Parser::token::AVAR) || (ptr->type == Parser::token::IMMVAR) ||
|
|
(ptr->type == Parser::token::IMMSVAR) || (ptr->type == Parser::token::UNDVAR));
|
|
|
|
if (is_valid_variable) {
|
|
sym_table->erase(sym_name);
|
|
delete ptr;
|
|
}
|
|
else {
|
|
warning("Variable '" + sym_name + "' not defined.\n", false);
|
|
}
|
|
}
|
|
|
|
symrec *Aprepro::getsym(const char *sym_name) const { return sym_table->getsym(sym_name); }
|
|
|
|
symrec *Aprepro::getsym(const std::string &sym_name) const { return getsym(sym_name.c_str()); }
|
|
|
|
void Aprepro::dumpsym(const char *type, bool doInternal) const
|
|
{
|
|
if (type[0] == 'v') {
|
|
dumpsym(Parser::token::VAR, doInternal);
|
|
}
|
|
else if (type[0] == 'f') {
|
|
dumpsym(Parser::token::FNCT, doInternal);
|
|
}
|
|
}
|
|
|
|
void Aprepro::dumpsym(int type, bool doInternal) const { dumpsym(type, nullptr, doInternal); }
|
|
|
|
void Aprepro::dumpsym_json() const
|
|
{
|
|
(*infoStream) << "\n{\n";
|
|
bool first = true;
|
|
|
|
for (const auto &ptr : get_sorted_sym_table()) {
|
|
if (!ptr->isInternal) {
|
|
if (ptr->type == Parser::token::VAR || ptr->type == Parser::token::IMMVAR) {
|
|
(*infoStream) << (first ? "\"" : ",\n\"") << ptr->name << "\": " << std::setprecision(10)
|
|
<< ptr->value.var;
|
|
first = false;
|
|
}
|
|
else if (ptr->type == Parser::token::UNDVAR) {
|
|
(*infoStream) << (first ? "\"" : ",\n\"") << ptr->name << "\": null";
|
|
first = false;
|
|
}
|
|
else if (ptr->type == Parser::token::SVAR || ptr->type == Parser::token::IMMSVAR) {
|
|
(*infoStream) << (first ? "\"" : ",\n\"") << ptr->name << "\": \"" << ptr->value.svar
|
|
<< "\"";
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
(*infoStream) << "\n}\n";
|
|
}
|
|
|
|
void Aprepro::dumpsym(int type, const char *pre, bool doInternal) const
|
|
{
|
|
std::string comment = getsym("_C_")->value.svar;
|
|
std::string spre;
|
|
|
|
if (pre) {
|
|
spre = pre;
|
|
}
|
|
|
|
if (type == Parser::token::VAR || type == Parser::token::SVAR || type == Parser::token::AVAR) {
|
|
(*infoStream) << "\n" << comment << " Variable = Value" << '\n';
|
|
|
|
int width = 10; // controls spacing/padding for the variable names
|
|
for (const auto &ptr : get_sorted_sym_table()) {
|
|
if (spre.empty() || ptr->name.find(spre) != std::string::npos) {
|
|
if (doInternal == ptr->isInternal) {
|
|
if (ptr->type == Parser::token::VAR) {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= " << std::setprecision(10) << ptr->value.var << "}" << '\n';
|
|
}
|
|
else if (ptr->type == Parser::token::IMMVAR) {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= " << std::setprecision(10) << ptr->value.var << "} (immutable)"
|
|
<< '\n';
|
|
}
|
|
else if (ptr->type == Parser::token::SVAR) {
|
|
if (strchr(ptr->value.svar.c_str(), '\n') != nullptr ||
|
|
strchr(ptr->value.svar.c_str(), '"') != nullptr) {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= '" << ptr->value.svar << "'}" << '\n';
|
|
}
|
|
else {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= \"" << ptr->value.svar << "\"}" << '\n';
|
|
}
|
|
}
|
|
else if (ptr->type == Parser::token::IMMSVAR) {
|
|
if (strchr(ptr->value.svar.c_str(), '\n') != nullptr ||
|
|
strchr(ptr->value.svar.c_str(), '"') != nullptr) {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= '" << ptr->value.svar << "'} (immutable)" << '\n';
|
|
}
|
|
else {
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t= \"" << ptr->value.svar << "\"} (immutable)" << '\n';
|
|
}
|
|
}
|
|
else if (ptr->type == Parser::token::AVAR) {
|
|
array *arr = ptr->value.avar;
|
|
(*infoStream) << comment << " {" << std::left << std::setw(width) << ptr->name
|
|
<< "\t (array) rows = " << arr->rows << ", cols = " << arr->cols << "} "
|
|
<< '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (type == Parser::token::FNCT || type == Parser::token::SFNCT ||
|
|
type == Parser::token::AFNCT) {
|
|
int fwidth = 20; // controls spacing/padding for the function names
|
|
(*infoStream) << trmclr::blue << "\nFunctions returning double:" << trmclr::normal << '\n';
|
|
for (const auto &ptr : get_sorted_sym_table()) {
|
|
if (spre.empty() || ptr->name.find(spre) != std::string::npos) {
|
|
if (ptr->type == Parser::token::FNCT) {
|
|
(*infoStream) << std::left << trmclr::green << std::setw(fwidth) << ptr->syntax
|
|
<< trmclr::normal << ": " << ptr->info << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
(*infoStream) << trmclr::blue << trmclr::blue
|
|
<< "\nFunctions returning string:" << trmclr::normal << '\n';
|
|
for (const auto &ptr : get_sorted_sym_table()) {
|
|
if (spre.empty() || ptr->name.find(spre) != std::string::npos) {
|
|
if (ptr->type == Parser::token::SFNCT) {
|
|
(*infoStream) << std::left << trmclr::green << std::setw(fwidth) << ptr->syntax
|
|
<< trmclr::normal << ": " << ptr->info << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
(*infoStream) << trmclr::blue << "\nFunctions returning array:" << trmclr::normal << '\n';
|
|
for (const auto &ptr : get_sorted_sym_table()) {
|
|
if (spre.empty() || ptr->name.find(spre) != std::string::npos) {
|
|
if (ptr->type == Parser::token::AFNCT) {
|
|
(*infoStream) << std::left << trmclr::green << std::setw(fwidth) << ptr->syntax
|
|
<< trmclr::normal << ": " << ptr->info << '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
|
|
|
void Aprepro::statistics(std::ostream *out) const
|
|
{
|
|
(*out) << "Statistics function no longer supported.\n";
|
|
}
|
|
|
|
void Aprepro::add_history(const std::string &original, const std::string &substitution)
|
|
{
|
|
if (!ap_options.keep_history) {
|
|
return;
|
|
}
|
|
|
|
if (!original.empty()) {
|
|
history_data hist{};
|
|
hist.original = original;
|
|
hist.substitution = substitution;
|
|
hist.index = outputStream.empty() ? std::streampos(0) : outputStream.top()->tellp();
|
|
|
|
history.push_back(hist);
|
|
}
|
|
}
|
|
|
|
const std::vector<history_data> &Aprepro::get_history() { return history; }
|
|
|
|
void Aprepro::clear_history()
|
|
{
|
|
if (ap_options.keep_history) {
|
|
history.clear();
|
|
}
|
|
}
|
|
|
|
std::vector<SEAMS::symrec *> Aprepro::get_sorted_sym_table() const
|
|
{
|
|
// We want the output to be sorted, so move all symbol pointers to a vector...
|
|
// Could pre-filter the vector, but for now, just copy all and filter afterwards...
|
|
std::vector<SEAMS::symrec *> vsym_table;
|
|
vsym_table.reserve(sym_table->get().size());
|
|
for (const auto &sym : sym_table->get()) {
|
|
vsym_table.push_back(sym.second);
|
|
}
|
|
std::sort(vsym_table.begin(), vsym_table.end(),
|
|
[](const auto &a, const auto &b) { return a->name < b->name; });
|
|
|
|
return vsym_table;
|
|
}
|
|
|
|
} // namespace SEAMS
|
|
|
|
namespace {
|
|
void output_copyright()
|
|
{
|
|
std::cerr << "\n\tCopyright (c) 2014-2017 National Technology & Engineering Solutions\n"
|
|
<< "of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with\n"
|
|
<< "NTESS, the U.S. Government retains certain rights in this software.\n"
|
|
<< "\n"
|
|
<< "\tRedistribution and use in source and binary forms, with or without\n"
|
|
<< "\tmodification, are permitted provided that the following conditions\n"
|
|
<< "\tare met:\n"
|
|
<< "\n"
|
|
<< "\t * Redistributions of source code must retain the above copyright\n"
|
|
<< "\t notice, this list of conditions and the following disclaimer.\n"
|
|
<< "\t * Redistributions in binary form must reproduce the above\n"
|
|
<< "\t copyright notice, this list of conditions and the following\n"
|
|
<< "\t disclaimer in the documentation and/or other materials provided\n"
|
|
<< "\t with the distribution.\n"
|
|
<< "\t * Neither the name of NTESS nor the names of its\n"
|
|
<< "\t contributors may be used to endorse or promote products derived\n"
|
|
<< "\t from this software without specific prior written permission.\n"
|
|
<< "\n"
|
|
<< "\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
|
|
<< "\t'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
|
|
<< "\tLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
|
|
<< "\tA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
|
|
<< "\tOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
|
|
<< "\tSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
|
|
<< "\tLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
|
|
<< "\tDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
|
|
<< "\tTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
|
|
<< "\t(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
|
|
<< "\tOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n";
|
|
}
|
|
} // namespace
|
|
|