3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-08 10:25:18 +00:00
z3/lib/ini_file.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

1565 lines
51 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
ini_file.cpp
Abstract:
Configuration file support.
Author:
Leonardo de Moura (leonardo) 2007-05-10.
Revision History:
--*/
#include"ini_file.h"
#include"util.h"
#include"trace.h"
#include"str_hashtable.h"
#include"map.h"
#include"string_buffer.h"
#include"symbol_table.h"
#include"error_codes.h"
#include<sstream>
template<typename T>
class value_vector_map {
void * m_owner;
map<T *, vector<T> *, ptr_hash<T>, ptr_eq<T> > m_mapping;
ptr_vector<T> m_domain;
ptr_vector<vector<T> > m_range;
unsigned m_max_size;
public:
value_vector_map(void * owner):m_owner(owner), m_max_size(0) {}
~value_vector_map() {
std::for_each(m_range.begin(), m_range.end(), delete_proc<vector<T> >());
}
void insert(T * ptr, T const & value) {
SASSERT(reinterpret_cast<size_t>(ptr) >= reinterpret_cast<size_t>(m_owner));
vector<T> * vect;
if (m_mapping.find(ptr, vect)) {
vect->push_back(value);
if (vect->size() > m_max_size)
m_max_size = vect->size();
return;
}
vect = alloc(vector<T>);
m_range.push_back(vect);
m_domain.push_back(ptr);
vect->push_back(value);
if (m_max_size == 0)
m_max_size = 1;
m_mapping.insert(ptr, vect);
}
void copy_params(void * curr_owner, unsigned idx) {
typename ptr_vector<T>::iterator it = m_domain.begin();
typename ptr_vector<T>::iterator end = m_domain.end();
for (; it != end; ++it) {
T * ptr = *it;
vector<T> * vect = 0;
m_mapping.find(ptr, vect);
SASSERT(vect != 0);
if (idx < vect->size()) {
// BIG HACK
SASSERT(reinterpret_cast<size_t>(ptr) >= reinterpret_cast<size_t>(m_owner));
size_t offset = reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_owner);
T * curr_ptr = reinterpret_cast<T *>(reinterpret_cast<char *>(curr_owner) + offset);
*curr_ptr = vect->operator[](idx);
}
}
}
unsigned size(void) const { return m_max_size; }
};
struct param_vector_imp {
value_vector_map<bool> m_bool_params;
value_vector_map<unsigned> m_unsigned_params;
value_vector_map<int> m_int_params;
value_vector_map<double> m_double_params;
value_vector_map<std::string> m_string_params;
value_vector_map<symbol> m_symbol_params;
value_vector_map<svector<symbol> > m_symbol_list_params;
value_vector_map<svector<symbol_nat_pair> > m_symbol_nat_list_params;
param_vector_imp(void * owner):
m_bool_params(owner),
m_unsigned_params(owner),
m_int_params(owner),
m_double_params(owner),
m_string_params(owner),
m_symbol_params(owner),
m_symbol_list_params(owner),
m_symbol_nat_list_params(owner) {
}
void insert_bool_param(bool * value_ptr, bool value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_bool_params.insert(value_ptr, value);
}
void insert_unsigned_param(unsigned * value_ptr, unsigned value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_unsigned_params.insert(value_ptr, value);
}
void insert_int_param(int * value_ptr, int value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_int_params.insert(value_ptr, value);
}
void insert_double_param(double * value_ptr, double value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_double_params.insert(value_ptr, value);
}
void insert_string_param(std::string * value_ptr, std::string const & value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_string_params.insert(value_ptr, value);
}
void insert_symbol_param(symbol * value_ptr, symbol const & value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> " << value << "\n";);
m_symbol_params.insert(value_ptr, value);
}
void insert_symbol_list_param(svector<symbol> * value_ptr, svector<symbol> const & value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> "; display(tout, value.begin(), value.end()); tout << "\n";);
m_symbol_list_params.insert(value_ptr, value);
}
void insert_symbol_nat_list_param(svector<symbol_nat_pair> * value_ptr, svector<symbol_nat_pair> const & value) {
TRACE("param_vector", tout << "insert: " << value_ptr << " -> "; display(tout, value.begin(), value.end()); tout << "\n";);
m_symbol_nat_list_params.insert(value_ptr, value);
}
void copy_params(void * curr_owner, unsigned idx) {
m_bool_params.copy_params(curr_owner, idx);
m_unsigned_params.copy_params(curr_owner, idx);
m_int_params.copy_params(curr_owner, idx);
m_double_params.copy_params(curr_owner, idx);
m_string_params.copy_params(curr_owner, idx);
m_symbol_params.copy_params(curr_owner, idx);
m_symbol_list_params.copy_params(curr_owner, idx);
m_symbol_nat_list_params.copy_params(curr_owner, idx);
}
unsigned size(void) const {
unsigned ret = 0;
ret = std::max(ret, m_bool_params.size());
ret = std::max(ret, m_unsigned_params.size());
ret = std::max(ret, m_int_params.size());
ret = std::max(ret, m_double_params.size());
ret = std::max(ret, m_string_params.size());
ret = std::max(ret, m_symbol_params.size());
ret = std::max(ret, m_symbol_list_params.size());
ret = std::max(ret, m_symbol_nat_list_params.size());
return ret;
}
};
param_vector::param_vector(void * owner):
m_ref_count(0) {
m_imp = alloc(param_vector_imp, owner);
}
param_vector::~param_vector() {
dealloc(m_imp);
}
void param_vector::inc_ref() {
m_ref_count++;
}
void param_vector::dec_ref() {
SASSERT(m_ref_count > 0);
m_ref_count--;
if (m_ref_count == 0)
dealloc(this);
}
void param_vector::copy_params(void * curr_owner, unsigned idx) {
m_imp->copy_params(curr_owner, idx);
}
unsigned param_vector::size(void) const
{
return m_imp->size();
}
enum itoken {
ITK_NULL, ITK_ID, ITK_NUM, ITK_DOUBLE, ITK_STRING, ITK_BAD_STRING, ITK_TRUE, ITK_FALSE, ITK_COMMA, ITK_LP, ITK_RP, ITK_LCB, ITK_RCB, ITK_CLN, ITK_EQ, ITK_BAD_ID, ITK_EOS, ITK_LAST
};
class ini_reserved_symbols {
typedef map<char const *, itoken, str_hash_proc, str_eq_proc> str2token;
str2token m_str2token;
public:
ini_reserved_symbols() {
m_str2token.insert("true", ITK_TRUE);
m_str2token.insert("false", ITK_FALSE);
}
itoken string2itoken(char const * str) {
str2token::entry * e = m_str2token.find_core(const_cast<char *>(str));
if (e)
return e->get_data().m_value;
else
return ITK_ID;
}
};
static char const * g_itoken2string[] = {
"<null>",
"<id>",
"<num>",
"<num-double>",
"<string>",
"<invalid-string>",
"true",
"false",
",",
"(",
")",
"{",
"}",
":",
"=",
"<invalid-id>",
"<end-of-stream>"
};
COMPILE_TIME_ASSERT(sizeof(g_itoken2string)/sizeof(char const*) == ITK_LAST);
inline static const char * itoken2string(itoken t) {
return g_itoken2string[t];
}
inline itoken string2itoken(ini_reserved_symbols& reserved, char const * str) {
itoken r = reserved.string2itoken(str);
TRACE("token", tout << str << " -> " << itoken2string(r) << "\n";);
return r;
}
class ini_lexer {
std::istream & m_input;
char m_curr_char;
int m_line;
int m_pos;
int m_tok_pos;
string_buffer<> m_buffer;
ini_reserved_symbols m_ini_reserved;
public:
bool eos() const {
return m_curr_char == EOF;
}
void next() {
m_curr_char = m_input.get();
m_pos++;
}
void save_and_next() {
m_buffer << m_curr_char;
next();
}
ini_lexer(std::istream & input):
m_input(input),
m_line(1),
m_pos(0),
m_tok_pos(0),
m_ini_reserved() {
next();
}
itoken read_num() {
while (isdigit(m_curr_char)) {
save_and_next();
}
if (m_curr_char == '.') {
save_and_next();
while (isdigit(m_curr_char)) {
save_and_next();
}
if (m_curr_char == 'e' || m_curr_char == 'E' || m_curr_char == 'd' || m_curr_char == 'D') {
save_and_next();
if (m_curr_char == '-') {
save_and_next();
}
while (isdigit(m_curr_char)) {
save_and_next();
}
}
return ITK_DOUBLE;
}
return ITK_NUM;
}
itoken read_id() {
while (!eos() && (isalpha(m_curr_char) || isdigit(m_curr_char) || m_curr_char == '_')) {
save_and_next();
}
return string2itoken(m_ini_reserved, m_buffer.c_str());
}
itoken read_string() {
m_tok_pos = m_pos;
next();
while (m_curr_char != '"' && m_curr_char != '\'') {
if (m_input.eof()) {
return ITK_BAD_STRING;
}
if (m_curr_char == '\n') {
return ITK_BAD_STRING;
}
if (m_curr_char == '\\') {
next(); // do not save the '\'
switch (m_curr_char) {
case 't':
m_buffer << '\t';
next();
break;
case 'n':
m_buffer << '\n';
next();
break;
case '\n':
m_buffer << '\n';
next();
break;
default:
if (!isdigit(m_curr_char)) {
save_and_next(); /* handles \\, \", \', and \? */
}
else { /* \xxx */
int c = 0;
int i = 0;
do {
c = 10*c + (m_curr_char-'0');
next();
}
while (++i<3 && isdigit(m_curr_char));
if (c > UCHAR_MAX) {
return ITK_BAD_STRING;
}
m_buffer << static_cast<char>(c);
}
}
}
else {
save_and_next();
}
}
next();
return ITK_STRING;
}
itoken next_token() {
for(;;) {
if (eos()) {
return ITK_EOS;
}
m_buffer.reset();
switch (m_curr_char) {
case ';': // comment
while (m_curr_char != '\n' && !eos()) {
next();
}
break;
case '\n':
next();
m_line++;
break;
case '=':
m_tok_pos = m_pos;
next();
return ITK_EQ;
case '\'':
case '\"':
return read_string();
case '{':
m_tok_pos = m_pos;
next();
return ITK_LCB;
case '}':
m_tok_pos = m_pos;
next();
return ITK_RCB;
case '(':
m_tok_pos = m_pos;
next();
return ITK_LP;
case ')':
m_tok_pos = m_pos;
next();
return ITK_RP;
case ',':
m_tok_pos = m_pos;
next();
return ITK_COMMA;
case ':':
m_tok_pos = m_pos;
next();
return ITK_CLN;
default:
if (isspace(m_curr_char)) {
next();
break;
}
else if (isdigit(m_curr_char)) {
m_tok_pos = m_pos;
save_and_next();
return read_num();
}
else {
char old = m_curr_char;
m_tok_pos = m_pos;
save_and_next();
TRACE("ini_lexer", tout << "old: " << static_cast<unsigned>(old) << " " << old << "\n";);
if (old == '-' && isdigit(m_curr_char)) {
return read_num();
}
else if (old == '_' || isalpha(old)) {
return read_id();
}
else {
return ITK_BAD_ID;
}
}
}
}
}
char const * get_token_data() const {
return m_buffer.c_str();
}
unsigned get_token_pos() const {
return m_tok_pos;
}
};
enum ini_param_kind {
IPK_BOOL,
IPK_INT,
IPK_UNSIGNED,
IPK_DOUBLE,
IPK_PERCENTAGE,
IPK_STRING,
IPK_SYMBOL,
IPK_SYMBOL_LIST,
IPK_SYMBOL_NAT_LIST
};
struct ini_param_info {
ini_param_kind m_kind;
bool m_is_mutable;
char const * m_description;
union {
struct {
int m_int_min;
int m_int_max;
int * m_int_val;
};
struct {
unsigned m_uint_min;
unsigned m_uint_max;
unsigned * m_uint_val;
};
bool * m_bool_val;
double * m_double_val;
double * m_perc_val;
symbol * m_sym_val;
std::string * m_str_val;
svector<symbol> * m_sym_list_val;
svector<symbol_nat_pair> * m_sym_nat_list_val;
};
ini_param_info(char const * descr = 0):
m_kind(IPK_BOOL),
m_is_mutable(false),
m_description(descr),
m_bool_val(0) {
}
ini_param_info(int min, int max, int * val, char const * descr, bool is_mutable):
m_kind(IPK_INT),
m_is_mutable(is_mutable),
m_description(descr),
m_int_min(min),
m_int_max(max),
m_int_val(val) {
}
ini_param_info(unsigned min, unsigned max, unsigned * val, char const * descr, bool is_mutable):
m_kind(IPK_UNSIGNED),
m_is_mutable(is_mutable),
m_description(descr),
m_uint_min(min),
m_uint_max(max),
m_uint_val(val) {
}
ini_param_info(bool * val, char const * descr, bool is_mutable):
m_kind(IPK_BOOL),
m_is_mutable(is_mutable),
m_description(descr),
m_bool_val(val) {
}
ini_param_info(bool perc, double * val, char const * descr, bool is_mutable):
m_kind(perc ? IPK_PERCENTAGE : IPK_DOUBLE),
m_is_mutable(is_mutable),
m_description(descr),
m_perc_val(val) {
}
ini_param_info(svector<symbol> * val, char const * descr, bool is_mutable):
m_kind(IPK_SYMBOL_LIST),
m_is_mutable(is_mutable),
m_description(descr),
m_sym_list_val(val) {
}
ini_param_info(svector<symbol_nat_pair> * val, char const * descr, bool is_mutable):
m_kind(IPK_SYMBOL_NAT_LIST),
m_is_mutable(is_mutable),
m_description(descr),
m_sym_nat_list_val(val) {
}
ini_param_info(symbol * s, char const * descr, bool is_mutable):
m_kind(IPK_SYMBOL),
m_is_mutable(is_mutable),
m_description(descr),
m_sym_val(s) {
}
ini_param_info(std::string * str, char const * descr, bool is_mutable):
m_kind(IPK_STRING),
m_is_mutable(is_mutable),
m_description(descr),
m_str_val(str) {
}
};
struct ini_params_imp {
bool m_abort_on_error;
ini_reserved_symbols m_ini_reserved;
ref<param_vector> m_param_vector;
symbol_table<ini_param_info> m_param_info;
bool m_is_frozen;
ini_params_imp(bool abort_on_error): m_abort_on_error(abort_on_error), m_is_frozen(false) {}
void freeze(bool f) { m_is_frozen = f; }
void register_param_vector(param_vector * pv) {
m_param_vector = pv;
}
void register_bool_param(symbol param_name, bool & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(&value, descr, is_mutable));
}
void register_unsigned_param(symbol param_name, unsigned min, unsigned max, unsigned & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(min, max, &value, descr, is_mutable));
}
void register_int_param(symbol param_name, int min, int max, int & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(min, max, &value, descr, is_mutable));
}
void register_percentage_param(symbol param_name, double & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(true, &value, descr, is_mutable));
}
void register_double_param(symbol param_name, double & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(false, &value, descr, is_mutable));
}
void register_string_param(symbol param_name, std::string & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(&value, descr, is_mutable));
}
void register_symbol_param(symbol param_name, symbol & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(&value, descr, is_mutable));
}
void register_symbol_list_param(symbol param_name, svector<symbol> & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(&value, descr, is_mutable));
}
void register_symbol_nat_list_param(symbol param_name, svector<symbol_nat_pair> & value, char const * descr, bool is_mutable) {
SASSERT(!m_param_info.contains(param_name));
m_param_info.insert(param_name, ini_param_info(&value, descr, is_mutable));
}
struct symbol_lt_proc {
bool operator()(const symbol & s1, const symbol & s2) const {
return strcmp(s1.bare_str(), s2.bare_str()) < 0;
}
};
void display_param_help(char const* param_id, std::ostream& out) const {
ini_param_info info;
if (m_param_info.find(symbol(param_id), info)) {
display_param_info(out, info);
}
else {
out << "option " << param_id << " does not exist";
}
}
void display_param_info(std::ostream& out, ini_param_info& info) const {
switch (info.m_kind) {
case IPK_BOOL:
out << " boolean";
out << ", default: " << (*info.m_bool_val ? "true" : "false");
break;
case IPK_INT:
out << " integer";
if (info.m_int_min > INT_MIN) {
out << ", min: " << info.m_int_min;
}
if (info.m_int_max < INT_MAX) {
out << ", max: " << info.m_int_max;
}
out << ", default: " << *info.m_int_val;
break;
case IPK_UNSIGNED:
out << " unsigned integer";
if (info.m_uint_min > 0) {
out << ", min: " << info.m_uint_min;
}
if (info.m_uint_max < INT_MAX) {
out << ", max: " << info.m_uint_max;
}
out << ", default: " << *info.m_uint_val;
break;
case IPK_DOUBLE:
out << " double";
out << ", default: " << *info.m_double_val;
break;
case IPK_PERCENTAGE:
out << " percentage";
out << ", default: " << *info.m_perc_val;
break;
case IPK_SYMBOL:
out << " symbol";
out << ", default: " << *info.m_sym_val;
break;
case IPK_STRING:
out << " string";
out << ", default: " << *info.m_str_val;
break;
case IPK_SYMBOL_LIST:
out << " list of symbols (strings)";
break;
case IPK_SYMBOL_NAT_LIST:
out << " list of pairs: symbols(strings) x unsigned";
break;
}
if (info.m_description) {
out << ", " << info.m_description << ".";
}
out << "\n";
}
void display_params(std::ostream & out) const {
svector<symbol> params;
m_param_info.get_dom(params);
std::sort(params.begin(), params.end(), symbol_lt_proc());
svector<symbol>::iterator it = params.begin();
svector<symbol>::iterator end = params.end();
for (; it != end; ++it) {
out << *it << ":";
ini_param_info info;
m_param_info.find(*it, info);
display_param_info(out, info);
}
}
void display_params_documentation(std::ostream & out) const {
out << "// AUTOMATICALLY GENERATED FILE - DO NOT EDIT\n\n"
<< "/**\n"
<< " \\page config INI parameters\n\n";
svector<symbol> params;
m_param_info.get_dom(params);
std::sort(params.begin(), params.end(), symbol_lt_proc());
svector<symbol>::iterator it = params.begin();
svector<symbol>::iterator end = params.end();
for (; it != end; ++it) {
out << "- \\c " << *it << ":";
ini_param_info info;
m_param_info.find(*it, info);
switch (info.m_kind) {
case IPK_BOOL:
out << " \\em boolean";
out << ", default: " << (*info.m_bool_val ? "\\c true" : "\\c false");
break;
case IPK_INT:
out << " \\em integer";
if (info.m_int_min > INT_MIN) {
out << ", min: \\c " << info.m_int_min;
}
if (info.m_int_max < INT_MAX) {
out << ", max: \\c " << info.m_int_max;
}
out << ", default: \\c " << *info.m_int_val;
break;
case IPK_UNSIGNED:
out << " \\em unsigned \\em integer";
if (info.m_uint_min > 0) {
out << ", min: \\c " << info.m_uint_min;
}
if (info.m_uint_max < INT_MAX) {
out << ", max: \\c " << info.m_uint_max;
}
out << ", default: \\c " << *info.m_uint_val;
break;
case IPK_DOUBLE:
out << " \\em double";
out << ", default: \\c " << *info.m_double_val;
break;
case IPK_PERCENTAGE:
out << " \\em percentage";
out << ", default: \\c " << *info.m_perc_val;
break;
case IPK_STRING:
out << " \\em string";
out << ", default: " << *info.m_str_val;
break;
case IPK_SYMBOL:
out << " \\em symbol";
out << ", default: " << *info.m_sym_val;
break;
case IPK_SYMBOL_LIST:
out << " \\em list \\em of \\em symbols \\em (strings)";
break;
case IPK_SYMBOL_NAT_LIST:
out << " \\em list \\em of \\em pairs: \\em symbols(strings) \\em x \\em unsigned";
break;
}
if (info.m_description) {
out << ", " << info.m_description << ".";
}
out << "\n\n";
}
out << "*/\n";
}
void error(char const * param_id, char const * msg) {
if (m_abort_on_error) {
verbose_stream() << "Error setting '" << param_id << "', reason: " << msg << "\n";
throw z3_error(ERR_INI_FILE);
}
else {
throw set_get_param_exception(param_id, msg);
}
}
symbol trim(char const * param_id) {
string_buffer<> m_buffer;
while (*param_id != 0 && !isspace(*param_id)) {
m_buffer.append(*param_id);
param_id++;
}
return symbol(m_buffer.c_str());
}
void set_param_value(char const * param_id, char const * param_value) {
TRACE("param_value", tout << param_id << " " << param_value << "\n";);
if (param_value == 0) {
error(param_id, "invalid (null) option");
}
symbol s(param_id);
ini_param_info info;
if (!m_param_info.find(s, info)) {
s = trim(param_id);
if (!m_param_info.find(s, info))
error(param_id, "unknown option.");
}
if (!info.m_is_mutable && m_is_frozen) {
error(param_id, "option value cannot be modified after initialization");
}
switch (info.m_kind) {
case IPK_BOOL:
parse_bool_param(param_id, info, param_value);
break;
case IPK_INT:
parse_int_param(param_id, info, param_value);
break;
case IPK_UNSIGNED:
parse_uint_param(param_id, info, param_value);
break;
case IPK_DOUBLE:
parse_double_param(param_id, info, param_value);
break;
case IPK_PERCENTAGE:
parse_perc_param(param_id, info, param_value);
break;
case IPK_STRING:
parse_string_param(param_id, info, param_value);
break;
case IPK_SYMBOL:
// TODO: not used so far
*info.m_sym_val = param_value;
break;
case IPK_SYMBOL_LIST:
error(param_id, "this option can only be set in an INI file");
break;
case IPK_SYMBOL_NAT_LIST:
error(param_id, "this option can only be set in an INI file");
break;
default:
UNREACHABLE();
}
}
bool get_param_value(char const * param_id, std::string& param_value) {
TRACE("param_value", tout << param_id << "\n";);
std::ostringstream buffer;
symbol s(param_id);
ini_param_info info;
if (!m_param_info.find(s, info)) {
s = trim(param_id);
if (!m_param_info.find(s, info)) {
return false;
}
}
switch (info.m_kind) {
case IPK_BOOL:
buffer << ((*info.m_bool_val)?"true":"false");
break;
case IPK_INT:
buffer << (*info.m_int_val);
break;
case IPK_UNSIGNED:
buffer << (*info.m_uint_val);
break;
case IPK_DOUBLE:
buffer << (*info.m_double_val);
break;
case IPK_PERCENTAGE:
buffer << (*info.m_perc_val);
break;
case IPK_STRING:
buffer << (*info.m_str_val);
break;
case IPK_SYMBOL:
buffer << (info.m_sym_val->str());
break;
case IPK_SYMBOL_LIST:
error(param_id, "this option cannot be retrieved");
break;
case IPK_SYMBOL_NAT_LIST:
error(param_id, "this option cannot be retrieved");
break;
default:
UNREACHABLE();
}
param_value = buffer.str();
return true;
}
string_buffer<> m_buffer;
char const * get_token_data() const {
return m_buffer.c_str();
}
void save_and_next(char const * & in) {
m_buffer.append(*in);
++in;
}
itoken read_num(char const * & in) {
while (isdigit(*in)) {
save_and_next(in);
}
TRACE("read_num", tout << "1. read_num: " << m_buffer.c_str() << ", *in: " << *in << "\n";);
if (*in == '.') {
save_and_next(in);
while (isdigit(*in)) {
save_and_next(in);
}
TRACE("read_num", tout << "2. read_num: " << m_buffer.c_str() << ", *in: " << *in << "\n";);
if (*in == 'e' || *in == 'E' || *in == 'd' || *in == 'D') {
save_and_next(in);
if (*in == '-') {
save_and_next(in);
}
while (isdigit(*in)) {
save_and_next(in);
}
}
return ITK_DOUBLE;
}
return ITK_NUM;
}
itoken read_id(char const * & in) {
while (!*in == 0 && (isalpha(*in) || isdigit(*in) || *in == '_')) {
save_and_next(in);
}
return string2itoken(m_ini_reserved, m_buffer.c_str());
}
itoken read_string(char const * & in) {
++in;
while (*in != '"' && *in != '\'') {
TRACE("read_string", tout << *in << "\n";);
if (*in == 0)
return ITK_BAD_STRING;
if (*in == '\n')
return ITK_BAD_STRING;
if (*in == '\\') {
++in;
switch (*in) {
case 't':
m_buffer << '\t';
++in;
break;
case 'n':
m_buffer << '\n';
++in;
break;
case '\n':
m_buffer << '\n';
++in;
break;
default:
if (!isdigit(*in)) {
save_and_next(in); /* handles \\, \", \', and \? */
}
else { /* \xxx */
int c = 0;
int i = 0;
do {
c = 10*c + (*in-'0');
++in;
}
while (++i<3 && isdigit(*in));
if (c > UCHAR_MAX)
return ITK_BAD_STRING;
m_buffer << static_cast<char>(c);
}
}
}
else {
save_and_next(in);
}
}
++in;
return ITK_STRING;
}
itoken next_token(char const * & in) {
for(;;) {
if (*in == 0)
return ITK_EOS;
m_buffer.reset();
switch (*in) {
case '{':
in++;
return ITK_LCB;
case '}':
in++;
return ITK_RCB;
case ',':
in++;
return ITK_COMMA;
case '"':
case '\'':
TRACE("read_string", tout << "found \"\n";);
return read_string(in);
default:
if (isspace(*in)) {
in++;
break;
}
else if (isdigit(*in)) {
TRACE("read_num", tout << "found is_digit\n";);
return read_num(in);
}
else {
char old = *in;
save_and_next(in);
if (old == '-' && isdigit(*in)) {
return read_num(in);
}
else if (old == '_' || isalpha(old)) {
return read_id(in);
}
else {
return ITK_BAD_ID;
}
}
}
}
}
bool end_of_list(char const * param_id, char const * & param_value) {
switch (next_token(param_value)) {
case ITK_COMMA:
return false;
case ITK_RCB:
return true;
default:
error(param_id, "boolean value (true/false) expected");
}
return false;
}
void parse_bool_param(char const * param_id, ini_param_info & info, char const * param_value) {
switch (next_token(param_value)) {
case ITK_TRUE:
*info.m_bool_val = true;
break;
case ITK_FALSE:
*info.m_bool_val = false;
break;
default:
error(param_id, "boolean value (true/false) expected");
}
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
switch (next_token(param_value)) {
case ITK_TRUE:
m_param_vector->m_imp->insert_bool_param(info.m_bool_val, true);
break;
case ITK_FALSE:
m_param_vector->m_imp->insert_bool_param(info.m_bool_val, false);
break;
default:
error(param_id, "boolean value (true/false) expected");
}
if (end_of_list(param_id, param_value))
return;
}
}
}
void parse_int_param(char const * param_id, ini_param_info & info, char const * param_value) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < info.m_int_min || val > info.m_int_max)
error(param_id, "integer out of bounds");
*info.m_int_val = val;
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < info.m_int_min || val > info.m_int_max)
error(param_id, "integer out of bounds");
m_param_vector->m_imp->insert_int_param(info.m_int_val, val);
if (end_of_list(param_id, param_value))
return;
}
}
}
void parse_uint_param(char const * param_id, ini_param_info & info, char const * param_value) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
unsigned val = static_cast<unsigned>(strtol(get_token_data(), 0, 10));
if (val < info.m_uint_min || val > info.m_uint_max)
error(param_id, "unsigned out of bounds");
*info.m_uint_val = val;
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
unsigned val = static_cast<unsigned>(strtol(get_token_data(), 0, 10));
if (val < info.m_uint_min || val > info.m_uint_max)
error(param_id, "unsigned out of bounds");
m_param_vector->m_imp->insert_unsigned_param(info.m_uint_val, val);
if (end_of_list(param_id, param_value))
return;
}
}
}
void parse_double_param(char const * param_id, ini_param_info & info, char const * param_value) {
itoken k = next_token(param_value);
if (k != ITK_NUM && k != ITK_DOUBLE)
error(param_id, "float expected");
char * aux;
*info.m_double_val = strtod(get_token_data(), &aux);
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
k = next_token(param_value);
if (k != ITK_NUM && k != ITK_DOUBLE)
error(param_id, "float expected");
m_param_vector->m_imp->insert_double_param(info.m_double_val, strtod(get_token_data(), &aux));
if (end_of_list(param_id, param_value))
return;
}
}
}
void parse_perc_param(char const * param_id, ini_param_info & info, char const * param_value) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < 0 || val > 100)
error(param_id, "integer between 0 and 100 expected");
*info.m_perc_val = static_cast<double>(val)/100.0;
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
if (next_token(param_value) != ITK_NUM)
error(param_id, "integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < 0 || val > 100)
error(param_id, "integer between 0 and 100 expected");
m_param_vector->m_imp->insert_double_param(info.m_perc_val, static_cast<double>(val)/100.0);
if (end_of_list(param_id, param_value))
return;
}
}
}
void parse_string_param(char const * param_id, ini_param_info & info, char const * param_value) {
if (next_token(param_value) != ITK_STRING)
error(param_id, "string expected");
*info.m_str_val = get_token_data();
if (m_param_vector.get() && next_token(param_value) == ITK_LCB) {
for (;;) {
if (next_token(param_value) != ITK_STRING)
error(param_id, "string expected");
m_param_vector->m_imp->insert_string_param(info.m_str_val, get_token_data());
if (end_of_list(param_id, param_value))
return;
}
}
}
};
class ini_parser {
ini_lexer m_lexer;
ini_params_imp * m_params;
itoken m_curr_token;
void error(unsigned pos, char const * msg) {
if (m_params->m_abort_on_error) {
verbose_stream() << "Error INI file [position: " << pos << "]: " << msg << "\n";
throw z3_error(ERR_INI_FILE);
}
else {
throw ini_parser_exception(pos, msg);
}
}
void error(char const * msg) {
error(m_lexer.get_token_pos(), msg);
}
itoken get_curr_token() {
if (m_curr_token == ITK_NULL) {
m_curr_token = m_lexer.next_token();
}
SASSERT(m_curr_token != ITK_NULL);
return m_curr_token;
}
void next() {
if (m_curr_token == ITK_NULL) {
m_lexer.next_token();
}
else {
m_curr_token = ITK_NULL;
}
}
char const * get_token_data() {
if (m_curr_token == ITK_NULL) {
get_curr_token();
}
SASSERT(m_curr_token != ITK_NULL);
return m_lexer.get_token_data();
}
bool test_next(itoken expected) {
if (get_curr_token() == expected) {
next();
return true;
}
else {
return false;
}
}
void check(itoken expected) {
if (!test_next(expected)) {
string_buffer<> msg;
msg << "unexpected token '" << itoken2string(get_curr_token())
<< "', '" << itoken2string(expected) << "' expected.";
error(msg.c_str());
}
}
public:
ini_parser(std::istream & in, ini_params_imp * p):
m_lexer(in),
m_params(p),
m_curr_token(ITK_NULL) {
}
void parse_param() {
symbol s(get_token_data());
check(ITK_ID);
ini_param_info info;
if (!m_params->m_param_info.find(s, info)) {
error("unknown option.");
}
check(ITK_EQ);
switch (info.m_kind) {
case IPK_BOOL:
parse_bool_param(info);
break;
case IPK_INT:
parse_int_param(info);
break;
case IPK_UNSIGNED:
parse_uint_param(info);
break;
case IPK_DOUBLE:
parse_double_param(info);
break;
case IPK_PERCENTAGE:
parse_perc_param(info);
break;
case IPK_STRING:
parse_string_param(info);
break;
case IPK_SYMBOL:
// TODO: add support for VAL{VAL,...,VAL}
*info.m_sym_val = get_token_data();
check(ITK_STRING);
break;
case IPK_SYMBOL_NAT_LIST:
parse_symbol_nat_list(info);
break;
case IPK_SYMBOL_LIST:
parse_symbol_list(info);
break;
default:
UNREACHABLE();
}
}
bool end_of_list() {
switch (get_curr_token()) {
case ITK_COMMA:
next();
return false;
case ITK_RCB:
next();
return true;
default:
error("boolean value expected");
}
return false;
}
void parse_bool_param(ini_param_info & info) {
if (get_curr_token() != ITK_TRUE && get_curr_token() != ITK_FALSE)
error("boolean value expected");
*info.m_bool_val = get_curr_token() == ITK_TRUE;
next();
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_TRUE && get_curr_token() != ITK_FALSE)
error("boolean value expected");
m_params->m_param_vector->m_imp->insert_bool_param(info.m_bool_val, get_curr_token() == ITK_TRUE);
next();
if (end_of_list())
return;
}
}
}
void parse_int_param(ini_param_info & info) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < info.m_int_min || val > info.m_int_max)
error("integer out of bounds");
*info.m_int_val = val;
next();
TRACE("int_param", tout << "val: " << val << "\n";);
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < info.m_int_min || val > info.m_int_max)
error("integer out of bounds");
m_params->m_param_vector->m_imp->insert_int_param(info.m_int_val, val);
next();
if (end_of_list())
return;
}
}
}
void parse_uint_param(ini_param_info & info) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
long val = strtol(get_token_data(), 0, 10);
if (val < static_cast<long>(info.m_uint_min) || val > static_cast<long>(info.m_uint_max))
error("integer out of bounds");
*info.m_uint_val = val;
next();
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
long val = strtol(get_token_data(), 0, 10);
if (val < static_cast<long>(info.m_uint_min) || val > static_cast<long>(info.m_uint_max))
error("integer out of bounds");
m_params->m_param_vector->m_imp->insert_unsigned_param(info.m_uint_val, val);
next();
if (end_of_list())
return;
}
}
}
void parse_double_param(ini_param_info & info) {
if (get_curr_token() != ITK_NUM && get_curr_token() != ITK_DOUBLE)
error("float expected");
char * aux;
*info.m_double_val = strtod(get_token_data(), &aux);
next();
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_NUM && get_curr_token() != ITK_DOUBLE)
error("float expected");
m_params->m_param_vector->m_imp->insert_double_param(info.m_double_val, strtod(get_token_data(), &aux));
next();
if (end_of_list())
return;
}
}
}
void parse_perc_param(ini_param_info & info) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < 0 || val > 100)
error("integer between 0 and 100 expected");
*info.m_perc_val = static_cast<double>(val)/100.0;
next();
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_NUM)
error("integer expected");
int val = strtol(get_token_data(), 0, 10);
if (val < 0 || val > 100)
error("integer between 0 and 100 expected");
m_params->m_param_vector->m_imp->insert_double_param(info.m_perc_val, static_cast<double>(val)/100.0);
next();
if (end_of_list())
return;
}
}
}
void parse_string_param(ini_param_info & info) {
if (get_curr_token() != ITK_STRING)
error("string expected");
*info.m_str_val = get_token_data();
next();
if (m_params->m_param_vector.get() && get_curr_token() == ITK_LCB) {
next();
for (;;) {
if (get_curr_token() != ITK_STRING)
error("string expected");
m_params->m_param_vector->m_imp->insert_string_param(info.m_str_val, get_token_data());
next();
if (end_of_list())
return;
}
}
}
void parse_s_list(svector<symbol> & result) {
check(ITK_LP);
for (;;) {
symbol s(get_token_data());
result.push_back(s);
check(ITK_ID);
if (!test_next(ITK_COMMA)) {
check(ITK_RP);
return;
}
}
}
void parse_symbol_list(ini_param_info & info) {
parse_s_list(*(info.m_sym_list_val));
if (m_params->m_param_vector.get() && test_next(ITK_LCB)) {
for (;;) {
svector<symbol> lst;
parse_s_list(lst);
m_params->m_param_vector->m_imp->insert_symbol_list_param(info.m_sym_list_val, lst);
if (!test_next(ITK_COMMA)) {
check(ITK_RCB);
return;
}
}
}
}
void parse_sn_list(svector<symbol_nat_pair> & result) {
check(ITK_LP);
for (;;) {
symbol s(get_token_data());
check(ITK_ID);
check(ITK_CLN);
unsigned val = strtol(get_token_data(), 0, 10);
check(ITK_NUM);
result.push_back(std::make_pair(s, val));
if (!test_next(ITK_COMMA)) {
check(ITK_RP);
return;
}
}
}
void parse_symbol_nat_list(ini_param_info & info) {
parse_sn_list(*(info.m_sym_nat_list_val));
if (m_params->m_param_vector.get() && test_next(ITK_LCB)) {
for (;;) {
svector<symbol_nat_pair> lst;
parse_sn_list(lst);
m_params->m_param_vector->m_imp->insert_symbol_nat_list_param(info.m_sym_nat_list_val, lst);
if (!test_next(ITK_COMMA)) {
check(ITK_RCB);
return;
}
}
}
}
void parse() {
while (get_curr_token() != ITK_EOS) {
parse_param();
}
}
};
ini_params::ini_params(bool abort_on_error) {
m_imp = alloc(ini_params_imp, abort_on_error);
}
ini_params::~ini_params() {
dealloc(m_imp);
}
void ini_params::register_bool_param(char const * param_name, bool & value, char const * descr, bool is_mutable) {
m_imp->register_bool_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_unsigned_param(char const * param_name, unsigned min, unsigned max, unsigned & value, char const * descr, bool is_mutable) {
m_imp->register_unsigned_param(symbol(param_name), min, max, value, descr, is_mutable);
}
void ini_params::register_int_param(char const * param_name, int min, int max, int & value, char const * descr, bool is_mutable) {
m_imp->register_int_param(symbol(param_name), min, max, value, descr, is_mutable);
}
void ini_params::register_double_param(char const * param_name, double & value, char const * descr, bool is_mutable) {
m_imp->register_double_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_percentage_param(char const * param_name, double & value, char const * descr, bool is_mutable) {
m_imp->register_percentage_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_string_param(char const * param_name, std::string & value, char const * descr, bool is_mutable) {
m_imp->register_string_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_symbol_param(char const * param_name, symbol & value, char const * descr, bool is_mutable) {
m_imp->register_symbol_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_symbol_list_param(char const * param_name, svector<symbol> & value, char const * descr, bool is_mutable) {
m_imp->register_symbol_list_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_symbol_nat_list_param(char const * param_name, svector<symbol_nat_pair> & value, char const * descr, bool is_mutable) {
m_imp->register_symbol_nat_list_param(symbol(param_name), value, descr, is_mutable);
}
void ini_params::register_param_vector(param_vector * pv) {
m_imp->register_param_vector(pv);
}
void ini_params::read_ini_file(std::istream & in) {
ini_parser p(in, m_imp);
p.parse();
}
void ini_params::display_params(std::ostream & out) const {
m_imp->display_params(out);
}
void ini_params::display_parameter_help(char const* param_id, std::ostream & out) const {
m_imp->display_param_help(param_id, out);
}
void ini_params::display_params_documentation(std::ostream & out) const {
m_imp->display_params_documentation(out);
}
void ini_params::set_param_value(char const * param_id, char const * param_value) {
m_imp->set_param_value(param_id, param_value);
}
bool ini_params::get_param_value(char const * param_id, std::string& param_value) {
return m_imp->get_param_value(param_id, param_value);
}
void ini_params::freeze(bool f) {
m_imp->freeze(f);
}