#define _CRT_SECURE_NO_DEPRECATE #include "config-parser.h" #include "str-com.h" #include <stdio.h> #include <string.h> #include <ctype.h> #include <stack> // // Implementation: helper classes // // ConfigParser::parserState nse::ConfigParser::parserState::parserState() { idx = 0; level = 0; nalloc = c_alloc_init; name_space = new char[c_alloc_init]; *name_space = '\0'; } nse::ConfigParser::parserState::parserState( const parserState& state) { idx = state.idx; level = state.level; nalloc = state.nalloc; name_space = new char[nalloc]; strcpy(name_space, state.name_space); } nse::ConfigParser::parserState::~parserState() { delete[] name_space; } void nse::ConfigParser::parserState::truncate_name_space() { truncate_name(name_space, '.'); } void nse::ConfigParser::parserState::append_name_space( const char* name) { append_name(&name_space, &nalloc, name, '.'); } // // ConfigParser::rpnList nse::ConfigParser::rpnList::rpnList() { nexpr = 0; nalloc = c_alloc_init; expr = new int[nalloc]; } nse::ConfigParser::rpnList::~rpnList() { delete[] expr; } void nse::ConfigParser::rpnList::init() { nexpr = 0; } void nse::ConfigParser::rpnList::add(const int idx) { if (nexpr >= nalloc) { int *buf = new int[nalloc + c_alloc_init]; memcpy(buf, expr, nalloc * sizeof(int)); delete[] expr; expr = buf; } expr[nexpr] = idx; nexpr++; } bool nse::ConfigParser::rpnList::empty() { return nexpr == 0; } bool nse::ConfigParser::rpnList::convert( parserState& state, const LEXEME_TYPE *lexeme_type, const FileParser& parser) { const int nlexeme = parser.get_ntokens(); std::stack<int> hstack; init(); LEXEME_TYPE p_lex = IS_INVALID, lex; bool status = true; while ((state.idx < nlexeme) && (p_lex != IS_SEMICOLON)) { lex = lexeme_type[state.idx]; if ((lex == IS_VALUE) || (lex == IS_NAME)) { // adding value | variable reference // - postpone existence check to evaluation step add(state.idx); } else if (lex == IS_BRACKET_OPEN) { hstack.push(state.idx); // pushing '(' to stack } else if (lex == IS_BRACKET_CLOSE) { // pop till '(' found & remove '(' from stack int idx; bool flag = false; while (!hstack.empty()) { idx = hstack.top(); hstack.pop(); if (lexeme_type[idx] == IS_BRACKET_OPEN) { flag = true; break; } else { add(idx); } } if (!flag) { printf(" CONFIG:> missing bracket '(' (line, %i)\n", parser.get_line_num(state.idx)); } else if (p_lex == IS_BRACKET_OPEN) { printf(" CONFIG:> null sub-expression found '()' (line, %i)\n", parser.get_line_num(state.idx)); flag = false; } status = status && flag; } else if (is_op(lex)) // handling operators { int idx; while (!hstack.empty()) { idx = hstack.top(); if (!is_op(lexeme_type[idx])) break; if ( ((op_associativity(lex) == IS_OP_LEFT) && (!op_lt(lexeme_type[idx], lex))) || ((op_associativity(lex) == IS_OP_RIGHT) && (op_lt(lex, lexeme_type[idx]))) ) { hstack.pop(); add(idx); } else break; } hstack.push(state.idx); } else if (lex == IS_SEMICOLON) { // removing elements until stack is empty or getting '(' int idx; bool flag = true; while (!hstack.empty()) { idx = hstack.top(); hstack.pop(); if (lexeme_type[idx] == IS_BRACKET_OPEN) { flag = false; break; } else { add(idx); } } if (!flag) { printf(" CONFIG:> missing bracket ')' (line, %i)\n", parser.get_line_num(state.idx)); } status = status && flag; } else { printf(" CONFIG:> unexpected lexeme in expression: '%s' (line, %i)\n", parser.get_token(state.idx), parser.get_line_num(state.idx)); status = false; } p_lex = lex; state.idx++; } status = status && (!empty()); return status; } // // Implementation: ConfigureParser // nse::ConfigParser::ConfigParser() { nvars = 0; nalloc_vars = c_alloc_init; var = new cfgVariable[nalloc_vars]; } nse::ConfigParser::~ConfigParser() { nvars = 0; nalloc_vars = 0; delete[] var; } bool nse::ConfigParser::is_op(const LEXEME_TYPE op) { return ( (op == IS_OP_ADD) || (op == IS_OP_SUB) || (op == IS_OP_MUL) || (op == IS_OP_DIV) || (op == IS_OP_MOD) || (op == IS_OP_PLUS) || (op == IS_OP_MINUS) || (op == IS_OP_EXP)); } bool nse::ConfigParser::is_op_binary(const LEXEME_TYPE op) { return ( (op == IS_OP_ADD) || (op == IS_OP_SUB) || (op == IS_OP_MUL) || (op == IS_OP_DIV) || (op == IS_OP_MOD) || (op == IS_OP_EXP)); } bool nse::ConfigParser::is_op_unary(const LEXEME_TYPE op) { return ((op == IS_OP_PLUS) || (op == IS_OP_MINUS)); } int nse::ConfigParser::op_priority( const LEXEME_TYPE op) { if ((op == IS_OP_ADD) || (op == IS_OP_SUB)) return 1; if ((op == IS_OP_MUL) || (op == IS_OP_DIV) || (op == IS_OP_MOD)) return 2; if ((op == IS_OP_PLUS) || (op == IS_OP_MINUS)) return 3; if ((op == IS_OP_EXP)) return 4; return 0; } nse::ConfigParser::OP_ASSOCIATIVITY nse::ConfigParser::op_associativity( const LEXEME_TYPE op) { if (op == IS_OP_EXP) return IS_OP_RIGHT; else return IS_OP_LEFT; } bool nse::ConfigParser::op_lt( const LEXEME_TYPE opA, const LEXEME_TYPE opB) { return (op_priority(opA) < op_priority(opB)); } bool nse::ConfigParser::is_valid_name(const char* token) { int token_length = strlen(token); if (token_length == 0) return false; if ((!isalpha(token[0])) && (token[0] != '_')) return false; for (int i = 1; i < token_length - 1; i++) { if ((!isalnum(token[i])) && (token[i] != '_') && (token[i] != '.')) return false; } if ((!isalnum(token[token_length - 1])) && (token[token_length - 1] != '_')) return false; return true; } bool nse::ConfigParser::add(const cfgVariable& ex) { if (!ex.is_valid_name()) return false; // just overwriting variable if already exists for (int k = 0; k < nvars; k++) { if (var[k].is_eq_name(ex)) { var[k] = ex; return true; } } if (nvars >= nalloc_vars) { cfgVariable *hvar = new cfgVariable[nalloc_vars + c_alloc_init]; for (int i = 0; i < nalloc_vars; i++) { hvar[i] = var[i]; } delete[] var; var = hvar; nalloc_vars += c_alloc_init; } var[nvars] = ex; nvars++; return true; } bool nse::ConfigParser::run_lexical_analysis( LEXEME_TYPE *lexeme_type, const FileParser& parser) { const int nlexeme = parser.get_ntokens(); if (nlexeme == 0) return true; bool status = true; for (int i = 0; i < nlexeme; i++) { const char* lexeme = parser.get_token(i); if (!strcmp(lexeme, "=")) lexeme_type[i] = IS_ASSIGNMENT; else if (!strcmp(lexeme, "{")) lexeme_type[i] = IS_BRACE_OPEN; else if (!strcmp(lexeme, "}")) lexeme_type[i] = IS_BRACE_CLOSE; else if (!strcmp(lexeme, "(")) lexeme_type[i] = IS_BRACKET_OPEN; else if (!strcmp(lexeme, ")")) lexeme_type[i] = IS_BRACKET_CLOSE; else if (!strcmp(lexeme, ";")) lexeme_type[i] = IS_SEMICOLON; else if (!strcmp(lexeme, "+")) { // checking for unary "+" if (i == 0) lexeme_type[i] = IS_OP_PLUS; else { if ((lexeme_type[i - 1] == IS_NAME) || (lexeme_type[i - 1] == IS_VALUE) || (lexeme_type[i - 1] == IS_BRACKET_CLOSE)) { lexeme_type[i] = IS_OP_ADD; } else lexeme_type[i] = IS_OP_PLUS; } } else if (!strcmp(lexeme, "-")) { // checking for unary "-" if (i == 0) lexeme_type[i] = IS_OP_MINUS; else { if ((lexeme_type[i - 1] == IS_NAME) || (lexeme_type[i - 1] == IS_VALUE) || (lexeme_type[i - 1] == IS_BRACKET_CLOSE)) { lexeme_type[i] = IS_OP_SUB; } else lexeme_type[i] = IS_OP_MINUS; } } else if (!strcmp(lexeme, "*")) lexeme_type[i] = IS_OP_MUL; else if (!strcmp(lexeme, "/")) lexeme_type[i] = IS_OP_DIV; else if (!strcmp(lexeme, "%")) lexeme_type[i] = IS_OP_MOD; else if (!strcmp(lexeme, "^")) lexeme_type[i] = IS_OP_EXP; else if (is_integer(lexeme) || is_double(lexeme) || is_valid_string(lexeme) || is_boolean(lexeme)) { lexeme_type[i] = IS_VALUE; } else if (is_valid_name(lexeme)) lexeme_type[i] = IS_NAME; else { lexeme_type[i] = IS_INVALID; printf(" CONFIG:> invalid lexeme: '%s' (line, %i)\n", lexeme, parser.get_line_num(i)); status = false; } } return status; } const nse::cfgVariable nse::ConfigParser::evaluate_rpn( const rpnList& rpn, parserState state, const LEXEME_TYPE *lexeme_type, const FileParser& parser) const { LEXEME_TYPE lex; std::stack<cfgVariable> dyn_expr; int eidx; for (int i = 0; i < rpn.nexpr; i++) { eidx = rpn.expr[i]; lex = lexeme_type[eidx]; if (is_op_binary(lex)) { if (dyn_expr.size() >= 2) { cfgVariable a = dyn_expr.top(); dyn_expr.pop(); cfgVariable b = dyn_expr.top(); dyn_expr.pop(); if (lex == IS_OP_ADD) b += a; else if (lex == IS_OP_SUB) b -= a; else if (lex == IS_OP_MUL) b *= a; else if (lex == IS_OP_DIV) b /= a; else if (lex == IS_OP_MOD) b %= a; else if (lex == IS_OP_EXP) b ^= a; else { printf(" CONFIG:> unknown binary operation: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return cfgVariable(); } if (!b.is_valid_type()) { printf(" CONFIG:> incorrect types of operands: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return b; } dyn_expr.push(b); } else { printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return cfgVariable(); } } else if (is_op_unary(lex)) { if (dyn_expr.size() >= 1) { cfgVariable op = dyn_expr.top(); dyn_expr.pop(); cfgVariable res; if (lex == IS_OP_PLUS) res = +op; else if (lex == IS_OP_MINUS) res = -op; else { printf(" CONFIG:> unknown unary operation: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return cfgVariable(); } if (!res.is_valid_type()) { printf(" CONFIG:> incorrect type of operand: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return res; } dyn_expr.push(res); } else { printf(" CONFIG:> insufficient number of arguments: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return cfgVariable(); } } else if (lex == IS_NAME) { cfgVariable arg; int pend = strlen(state.name_space); state.append_name_space(parser.get_token(eidx)); if (is_varname(state.name_space)) arg = get_variable(state.name_space); else { if (is_varname(parser.get_token(eidx))) { arg = get_variable(parser.get_token(eidx)); } else { printf(" CONFIG:> reference to undefined variable: '%s' (line, %i)\n", parser.get_token(eidx), parser.get_line_num(eidx)); return cfgVariable(); } } state.name_space[pend] = '\0'; // removing added name resolution dyn_expr.push(arg); } else { dyn_expr.push( cfgVariable(parser.get_token(eidx))); } } if (dyn_expr.size() != 1) return cfgVariable(); return cfgVariable(dyn_expr.top()); // delay res type checking [in add] } bool nse::ConfigParser::run_syntax_analysis( const LEXEME_TYPE *lexeme_type, const FileParser& parser) { const int nlexeme = parser.get_ntokens(); if (nlexeme == 0) return true; parserState state; rpnList rpn; bool status = true; while (state.idx < nlexeme) { // - closing namespace [}] if ((lexeme_type[state.idx] == IS_BRACE_CLOSE) && (state.level > 0)) { state.truncate_name_space(); state.level--; state.idx++; continue; } if (lexeme_type[state.idx] == IS_NAME) { if (state.idx + 1 < nlexeme) { // - open namespace [name {] if (lexeme_type[state.idx + 1] == IS_BRACE_OPEN) { // no dots allowed in namespace name at least for now if (strchr(parser.get_token(state.idx), '.') == NULL) { state.append_name_space(parser.get_token(state.idx)); } else { status = false; printf(" CONFIG:> '.' not allowed in namespaces: '%s' (line, %i)\n", parser.get_token(state.idx), parser.get_line_num(state.idx)); } state.level++; state.idx += 2; continue; } // - assignment [name = value|string ;] if (lexeme_type[state.idx + 1] == IS_ASSIGNMENT) { int varidx = state.idx; state.idx += 2; // setting state at expression // int pend = strlen(state.name_space); // converting to postfix notation if (!rpn.convert(state, lexeme_type, parser)) { state.append_name_space(parser.get_token(varidx)); status = false; printf(" CONFIG:> failed to process expression for variable: '%s' (line, %i)\n", state.name_space, parser.get_line_num(varidx)); state.name_space[pend] = '\0'; // removing added name resolution continue; } // evaluating postix expression (need namespace - not full variable name) cfgVariable value = evaluate_rpn(rpn, state, lexeme_type, parser); if (!value.is_valid_type()) { state.append_name_space(parser.get_token(varidx)); status = false; printf(" CONFIG:> failed to evaluate expression for variable: '%s' (line, %i)\n", state.name_space, parser.get_line_num(varidx)); state.name_space[pend] = '\0'; // removing added name resolution continue; } state.append_name_space(parser.get_token(varidx)); value.change_name(state.name_space); if (!add(value)) { status = false; printf(" CONFIG:> failed to add variable: '%s' (line, %i)\n", state.name_space, parser.get_line_num(varidx)); } state.name_space[pend] = '\0'; // removing added name resolution continue; } } status = false; printf(" CONFIG:> expecting '=' or '{' for name: '%s' (line, %i)\n", parser.get_token(state.idx), parser.get_line_num(state.idx)); state.idx += 2; continue; } status = false; printf(" CONFIG:> unexpected lexeme: '%s' (line, %i)\n", parser.get_token(state.idx), parser.get_line_num(state.idx)); state.idx++; } if (state.level > 0) { status = false; printf(" CONFIG:> unclosed namespaces, missing '}'\n"); } return status; } bool nse::ConfigParser::run(const char* filename) { // removing elements in config -- but keeping memory nvars = 0; FileParser parser; if (!parser.run(filename, '#', "={};()+-*/%^", true)) { printf(" CONFIG:> parsing failed for file: '%s'\n", filename); return false; } const int nlexeme = parser.get_ntokens(); if (nlexeme == 0) return true; LEXEME_TYPE *lexeme_type = new LEXEME_TYPE[nlexeme]; bool status_lexical = run_lexical_analysis(lexeme_type, parser); bool status_syntax = run_syntax_analysis(lexeme_type, parser); delete[] lexeme_type; return status_lexical & status_syntax; } bool nse::ConfigParser::mpi_run(const char* filename, const MPI_Comm comm) { const int host_rank = 0; int rank, status = 0; MPI_Comm_rank(comm, &rank); if (rank == host_rank) { if (run(filename)) status = 1; } mpi_broadcast(&status, 1, host_rank); return (status == 1); } bool nse::ConfigParser::mpi_run(const char* filename) { return mpi_run(filename, MPI_COMM_WORLD); } bool nse::ConfigParser::is_varname(const char* name) const { for (int i = 0; i < nvars; i++) { if (var[i].is_varname(name)) return true; } return false; } const nse::cfgVariable nse::ConfigParser::get_variable(const int idx) const { if ((idx < 0) || (idx >= nvars)) return cfgVariable(); return var[idx]; } const nse::cfgVariable nse::ConfigParser::get_variable(const char* name) const { for (int k = 0; k < nvars; k++) { if (var[k].is_varname(name)) return var[k]; } return cfgVariable(); } void nse::ConfigParser::print() const { for (int i = 0; i < nvars; i++) { var[i].print(); getc(stdin); } } bool nse::ConfigParser::get_value(const char* name, int* value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) return (var.get_value(value) > 0); else if (type == cfgVariable::IS_DOUBLE) { printf(" CONFIG:> failed to set (int) variable: '%s' - declared as double\n", name); return false; } else if (type == cfgVariable::IS_STRING) { printf(" CONFIG:> failed to set (int) variable: '%s' - declared as string\n", name); return false; } else if (type == cfgVariable::IS_BOOLEAN) { printf(" CONFIG:> failed to set (int) variable: '%s' - declared as boolean\n", name); return false; } printf(" CONFIG:> failed to set (int) variable: '%s'\n", name); return false; } bool nse::ConfigParser::get_value(const char* name, float* value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) { int ival; int status = var.get_value(&ival); (*value) = (float)ival; return (status > 0); } else if (type == cfgVariable::IS_DOUBLE) { double dval; int status = var.get_value(&dval); (*value) = (float)dval; return (status > 0); } else if (type == cfgVariable::IS_STRING) { printf(" CONFIG:> failed to set (float) variable: '%s' - declared as string\n", name); return false; } else if (type == cfgVariable::IS_BOOLEAN) { printf(" CONFIG:> failed to set (float) variable: '%s' - declared as boolean\n", name); return false; } printf(" CONFIG:> failed to set (float) variable: '%s' - undefined\n", name); return false; } bool nse::ConfigParser::get_value(const char* name, double* value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) { int ival; int status = var.get_value(&ival); (*value) = (double)ival; return (status > 0); } else if (type == cfgVariable::IS_DOUBLE) return (var.get_value(value) > 0); else if (type == cfgVariable::IS_STRING) { printf(" CONFIG:> failed to set (double) variable: '%s' - declared as string\n", name); return false; } else if (type == cfgVariable::IS_BOOLEAN) { printf(" CONFIG:> failed to set (double) variable: '%s' - declared as boolean\n", name); return false; } printf(" CONFIG:> failed to set (double) variable: '%s' - undefined\n", name); return false; } bool nse::ConfigParser::get_value(const char* name, char** value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as int\n", name); return false; } else if (type == cfgVariable::IS_DOUBLE) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as double\n", name); return false; } else if (type == cfgVariable::IS_STRING) return (var.get_value(value) > 0); else if (type == cfgVariable::IS_BOOLEAN) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as boolean\n", name); return false; } printf(" CONFIG:> failed to set (string) variable: '%s' - undefined\n", name); return false; } bool nse::ConfigParser::get_value(const char* name, std::string& value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as int\n", name); return false; } else if (type == cfgVariable::IS_DOUBLE) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as double\n", name); return false; } else if (type == cfgVariable::IS_STRING) return (var.get_value(value) > 0); else if (type == cfgVariable::IS_BOOLEAN) { printf(" CONFIG:> failed to set (string) variable: '%s' - declared as boolean\n", name); return false; } printf(" CONFIG:> failed to set (string) variable: '%s' - undefined\n", name); return false; } bool nse::ConfigParser::get_value(const char* name, bool* value) const { cfgVariable var = get_variable(name); cfgVariable::VAR_TYPE type = var.get_type(); if (type == cfgVariable::IS_INT) { printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as int\n", name); return false; } else if (type == cfgVariable::IS_DOUBLE) { printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as double\n", name); return false; } else if (type == cfgVariable::IS_STRING) { printf(" CONFIG:> failed to set (bool) variable: '%s' - declared as string\n", name); return false; } else if (type == cfgVariable::IS_BOOLEAN) return (var.get_value(value) > 0); printf(" CONFIG:> failed to set (bool) variable: '%s'\n", name); return false; } bool nse::ConfigParser::mpi_is_varname(const char* name, const MPI_Comm comm) const { const int host_rank = 0; int rank; bool status; MPI_Comm_rank(comm, &rank); if (rank == host_rank) { status = is_varname(name); } mpi_broadcast(&status, 1, host_rank, comm); return status; } template< typename T > bool nse::ConfigParser::mpi_get_value(const char* name, T* value, const MPI_Comm comm) const { const int host_rank = 0; int rank, status = 0; MPI_Comm_rank(comm, &rank); if (rank == host_rank) { if (get_value(name, value)) status = 1; } mpi_broadcast(&status, 1, host_rank, comm); if (status) mpi_broadcast(value, 1, host_rank, comm); return (status == 1); } bool nse::ConfigParser::mpi_get_value(const char* name, char** value, const MPI_Comm comm) const { const int host_rank = 0; int rank, status = 0; MPI_Comm_rank(comm, &rank); if (rank == host_rank) { if (get_value(name, value)) status = strlen(*value) + 1; } mpi_broadcast(&status, 1, host_rank, comm); if (status) { if (rank != host_rank) (*value) = new char[status]; mpi_broadcast(*value, status, host_rank, comm); } return (status > 0); } bool nse::ConfigParser::mpi_get_value(const char* name, std::string& value, const MPI_Comm comm) const { char* c_value; if (!mpi_get_value(name, &c_value, comm)) return false; value = std::string(c_value); delete[] c_value; return true; } template bool nse::ConfigParser::mpi_get_value(const char* name, int* value, const MPI_Comm comm) const; template bool nse::ConfigParser::mpi_get_value(const char* name, float* value, const MPI_Comm comm) const; template bool nse::ConfigParser::mpi_get_value(const char* name, double* value, const MPI_Comm comm) const; template bool nse::ConfigParser::mpi_get_value(const char* name, bool* value, const MPI_Comm comm) const;