Skeleton of application, at least compiles
This commit is contained in:
@@ -0,0 +1,463 @@
|
||||
#include "configparser.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int OPERATIONSET = 0;
|
||||
const int OPERATIONADD = 1;
|
||||
const int OPERATION_MERGE = 2;
|
||||
|
||||
// конструктор ConfigParam
|
||||
ConfigParam::ConfigParam()
|
||||
{
|
||||
name = "";
|
||||
this->val.clear();
|
||||
}
|
||||
|
||||
// создаем ConfigParam из строки
|
||||
ConfigParam::ConfigParam(string s)
|
||||
{
|
||||
name = s;
|
||||
this->val.clear();
|
||||
}
|
||||
|
||||
// создаем ConfigParam по имени и значению
|
||||
ConfigParam::ConfigParam(string name, string value)
|
||||
{
|
||||
this->name = name;
|
||||
this->val.clear();
|
||||
ConfigValue v;
|
||||
v.type = CONFIGVALUETYPESTRING;
|
||||
v.sval = value;
|
||||
this->val.push_back(v);
|
||||
}
|
||||
|
||||
bool ConfigParser::IsDelim(char c)
|
||||
{
|
||||
if (IsWS(c)) return true;
|
||||
if ((c == '{') || (c == '}')) return true;
|
||||
if ((c == '[') || (c == ']')) return true;
|
||||
if (c == ',') return true;
|
||||
if (c == ';') return true;
|
||||
if (c == '#') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator<(const ConfigParam &a, const ConfigParam &b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
}
|
||||
|
||||
string ConfigParser::i2s(int a)
|
||||
{
|
||||
ostringstream oss;
|
||||
oss << a;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Является ли символ whitespace-ом
|
||||
bool ConfigParser::IsWS(char c)
|
||||
{
|
||||
return ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'));
|
||||
}
|
||||
|
||||
// Можно ли такие символы использовать в названии параметров
|
||||
bool ConfigParser::GoodForName(char c)
|
||||
{
|
||||
if (IsWS(c)) return false;
|
||||
if ((c == '[') || (c == ']')) return false;
|
||||
if ((c == '{') || (c == '}')) return false;
|
||||
if ((c == '(') || (c == ')')) return false;
|
||||
if (c == '"') return false;
|
||||
if (c == '\\') return false;
|
||||
if (c == '+') return false;
|
||||
if (c == '=') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// возвращает символ, соответствующий escape-последовательности "\c"
|
||||
char ConfigParser::UnSlash(char c)
|
||||
{
|
||||
if (c == 'n') return '\n';
|
||||
if (c == '\\') return '\\';
|
||||
if (c == '\"') return '"';
|
||||
return c;
|
||||
}
|
||||
|
||||
// разбирает s с l по r, считая, что она должна быть ConfigParam - ом
|
||||
// возвращает, какая операция была применена
|
||||
int ConfigParser::ParseConfigParam(int &l, int &r, ConfigParam &p)
|
||||
{
|
||||
while ((l <= r) && (IsWS(s[l])))
|
||||
{
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r)
|
||||
throw "Expected parameter at line " + i2s(cline);
|
||||
if (!GoodForName(s[l]))
|
||||
{
|
||||
throw "Parameter name expected at line " + i2s(cline);
|
||||
}
|
||||
p.name = "";
|
||||
while (GoodForName(s[l]))
|
||||
{
|
||||
if (l == r) throw "Unfinished param at line " + i2s(cline);
|
||||
p.name += s[l];
|
||||
l++;
|
||||
}
|
||||
while ((l <= r) && (IsWS(s[l])))
|
||||
{
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r) throw "Unfinished param at line " + i2s(cline);
|
||||
int op;
|
||||
if (s[l] == '+')
|
||||
{
|
||||
if ((l == r) || (s[l + 1] != '=')) throw "Unrecognized operation: + or += expected at line " + i2s(cline);
|
||||
op = OPERATIONADD;
|
||||
l += 2;
|
||||
}
|
||||
else if (s[l] == '=')
|
||||
{
|
||||
op = OPERATIONSET;
|
||||
l++;
|
||||
}
|
||||
else if (s[l] == '(')
|
||||
{
|
||||
// this is a function call
|
||||
l++;
|
||||
string args = "";
|
||||
while (l <= r && s[l] != ')')
|
||||
{
|
||||
args += s[l];
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r) throw (string)("Unfinished function args at line " + i2s(cline));
|
||||
l++;
|
||||
if (p.name == "include")
|
||||
{
|
||||
// пока так костыльно, годный парсинг аргументов надо вместе с глобальным
|
||||
// рефакторингом парсера делать
|
||||
TrimSpaces(args);
|
||||
TrimQuotes(args);
|
||||
ConfigParser t;
|
||||
t.Load(args);
|
||||
p.val.push_back(t.cfg);
|
||||
// в этом случае значение имеет тип "множество"
|
||||
p.val[0].type = CONFIGVALUETYPESET;
|
||||
return OPERATION_MERGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Unknown function " + p.name;
|
||||
}
|
||||
}
|
||||
else throw "Unrecognized operation: + or += expected at line " + i2s(cline);
|
||||
while ((l <= r) && (IsWS(s[l])))
|
||||
{
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r) throw "Expected parameter value " + i2s(cline);
|
||||
if (s[l] == '{')
|
||||
{
|
||||
ConfigValue a;
|
||||
p.val.push_back(a);
|
||||
l++;
|
||||
// в этом случае значение имеет тип "множество"
|
||||
p.val[0].type = CONFIGVALUETYPESET;
|
||||
ParseConfigValue(l, r, p.val[0]);
|
||||
}
|
||||
else if (s[l] == '[')
|
||||
{
|
||||
l++;
|
||||
ParseConfigParamValues(l, r, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigValue a;
|
||||
p.val.push_back(a);
|
||||
// в этом случае значение имеет тип "строка"
|
||||
p.val[0].type = CONFIGVALUETYPESTRING;
|
||||
ParseConfigValue(l, r, p.val[0]);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
// Разбирает несколько ConfigValues, заданных через запятую
|
||||
void ConfigParser::ParseConfigParamValues(int &l, int &r, ConfigParam &b)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
while ((l <= r) && (IsWS(s[l])))
|
||||
{
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r) throw "Unfinished array at line " + i2s(cline);
|
||||
if (s[l] == ']')
|
||||
{
|
||||
l++;
|
||||
break;
|
||||
}
|
||||
ConfigValue a;
|
||||
b.val.push_back(a);
|
||||
if (s[l] == '{')
|
||||
{
|
||||
l++;
|
||||
b.val[b.val.size() - 1].type = CONFIGVALUETYPESET;
|
||||
}
|
||||
else
|
||||
{
|
||||
b.val[b.val.size() - 1].type = CONFIGVALUETYPESTRING;
|
||||
}
|
||||
ParseConfigValue(l, r, b.val[b.val.size() - 1]);
|
||||
if (l > r) throw "Unfinished array at line " + i2s(cline);
|
||||
if (s[l] == ']')
|
||||
{
|
||||
l++;
|
||||
break;
|
||||
}
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
||||
// разбирает строку s с l по r, считая, что она - корректный ConfigValue
|
||||
void ConfigParser::ParseConfigValue(int &l, int &r, ConfigValue &vl)
|
||||
{
|
||||
while ((l <= r) && (IsWS(s[l])))
|
||||
{
|
||||
if (s[l] == '\n') cline++;
|
||||
l++;
|
||||
}
|
||||
if (l > r) return;
|
||||
// отсечем комментарии
|
||||
if ((s[l] == ';') || (s[l] == '#'))
|
||||
{
|
||||
while (s[l] != '\n') l++;
|
||||
ParseConfigValue(l, r, vl);
|
||||
return;
|
||||
}
|
||||
// если в точке l наличествует символ-разделитель, значит
|
||||
// наше Value закончилось
|
||||
if ((s[l] == ',') || (s[l] == ']'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (vl.type == CONFIGVALUETYPESET)
|
||||
{
|
||||
// нашли скобочку - set закрылся
|
||||
if (s[l] == '}')
|
||||
{
|
||||
l++;
|
||||
return;
|
||||
}
|
||||
if (!GoodForName(s[l]))
|
||||
{
|
||||
throw "Parameter name expected at line " + i2s(cline);
|
||||
}
|
||||
ConfigParam p;
|
||||
int oper = ParseConfigParam(l, r, p);
|
||||
if (oper == OPERATION_MERGE)
|
||||
{
|
||||
for (set<ConfigParam>::iterator t = p.val[0].pval.begin(); t != p.val[0].pval.end(); t++)
|
||||
{
|
||||
vl.pval.insert(*t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
set<ConfigParam>::iterator it = vl.pval.find(p);
|
||||
if (it == vl.pval.end())
|
||||
{
|
||||
vl.pval.insert(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
if (oper == OPERATIONADD)
|
||||
{
|
||||
reverse(p.val.begin(), p.val.end());
|
||||
for (i = ((int)it->val.size() - 1); i >= 0; i--)
|
||||
{
|
||||
p.val.push_back(it->val[i]);
|
||||
}
|
||||
reverse(p.val.begin(), p.val.end());
|
||||
vl.pval.erase(it);
|
||||
vl.pval.insert(p);
|
||||
}
|
||||
else if (oper == OPERATIONSET)
|
||||
{
|
||||
vl.pval.erase(it);
|
||||
vl.pval.insert(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (string)("Internal error (wrong operation returned) at line " + i2s(cline));
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseConfigValue(l, r, vl);
|
||||
}
|
||||
else
|
||||
{
|
||||
vl.sval = "";
|
||||
int isslashed = 0;
|
||||
int isquoted = 0;
|
||||
while (1)
|
||||
{
|
||||
if (l > r) throw "Unfinished value at line " + i2s(cline);
|
||||
if (isslashed)
|
||||
{
|
||||
vl.sval += UnSlash(s[l]);
|
||||
isslashed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s[l] == '\\') isslashed = 1;
|
||||
else if (s[l] == '"') isquoted = 1 - isquoted;
|
||||
else
|
||||
{
|
||||
if (IsDelim(s[l]))
|
||||
{
|
||||
if (isquoted == 0) break;
|
||||
if (s[l] == '\n') throw "Unexpected end of line at line " + i2s(cline);
|
||||
}
|
||||
vl.sval += s[l];
|
||||
}
|
||||
}
|
||||
l++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// убирает двойные кавычки в начале/конце строки
|
||||
void ConfigParser::TrimQuotes(string &s)
|
||||
{
|
||||
if (((int)s.length()) < 2) return;
|
||||
if ((s[0] == '\"') && (s[((int)s.length()) - 1] == '\"'))
|
||||
{
|
||||
s = s.substr(1, s.length() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigParser::Load(string fname)
|
||||
{
|
||||
// Загружаем весь конфиг в одну строку, теперь будем ее разбирать
|
||||
FILE *fin = fopen(fname.c_str(), "rt");
|
||||
if (!fin) throw "Config file " + fname + " not found!";
|
||||
int c = getc(fin);
|
||||
slen = 0;
|
||||
while (c != EOF)
|
||||
{
|
||||
slen++;
|
||||
c = getc(fin);
|
||||
}
|
||||
s = new char[slen + 1];
|
||||
int i;
|
||||
fclose(fin);
|
||||
fin = fopen(fname.c_str(), "rt");
|
||||
for (i = 0; i < slen; i++) s[i] = getc(fin);
|
||||
fclose(fin);
|
||||
s[slen] = '\n';
|
||||
slen++;
|
||||
// Теперь считаем всю строку конфиг-значением (так оно и должно быть)
|
||||
cline = 1;
|
||||
cfg.type = CONFIGVALUETYPESET;
|
||||
cfg.sval = "";
|
||||
cfg.pval.clear();
|
||||
try{
|
||||
int l = 0;
|
||||
int r = slen - 1;
|
||||
ParseConfigValue(l, r, cfg);
|
||||
delete []s;
|
||||
}
|
||||
catch(string ss)
|
||||
{
|
||||
delete []s;
|
||||
throw "Error parsing " + fname + ": " + ss;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
delete []s;
|
||||
throw "Unrecognized error parsing " + fname;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// возвращает значение параметра (в старом стиле)
|
||||
string ConfigParser::GetParam(string name)
|
||||
{
|
||||
set<ConfigParam>::iterator p;
|
||||
p = this->cfg.pval.find(ConfigParam(name));
|
||||
if (p != this->cfg.pval.end())
|
||||
{
|
||||
if (p->val.size() == 0) throw "There are no values for parameter " + name;
|
||||
return p->val[0].sval;
|
||||
}
|
||||
throw ((string)"No param \"" + name + "\" found!");
|
||||
}
|
||||
|
||||
// возвращает значение параметра (в старом стиле)
|
||||
string ConfigParser::GetParamSilent(string name)
|
||||
{
|
||||
set<ConfigParam>::iterator p;
|
||||
p = this->cfg.pval.find(ConfigParam(name));
|
||||
if (p != this->cfg.pval.end())
|
||||
{
|
||||
if (p->val.size() == 0) return "";
|
||||
return p->val[0].sval;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// возвращает какое-то одно значение параметра
|
||||
// генерирует исключение при ошибке
|
||||
string ConfigParam::GetSingleVal() const
|
||||
{
|
||||
if (val.size() == 0) throw ((string)"No value available");
|
||||
if (val[0].type == CONFIGVALUETYPESTRING) return val[0].sval;
|
||||
else
|
||||
{
|
||||
throw "Wrong parameter value type (string expected)";
|
||||
}
|
||||
}
|
||||
|
||||
// убивает в строке пробелы в начале и в конце
|
||||
void ConfigParser::TrimSpaces(string &s)
|
||||
{
|
||||
string tmp = s;
|
||||
s = "";
|
||||
int wf = 0, i;
|
||||
for (i = 0; i < ((int)tmp.length()); i++)
|
||||
{
|
||||
if (! IsWS(tmp[i]) )
|
||||
{
|
||||
wf = 1;
|
||||
}
|
||||
if (wf == 1) s += tmp[i];
|
||||
}
|
||||
while ((s.length() > 0) && (IsWS(s[s.length() - 1])))
|
||||
{
|
||||
s.erase(--s.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Ищет в этом value заданный параметр. Возвращает пустую строку, если такого нет.
|
||||
string ConfigValue::GetParamVal(string pname) const
|
||||
{
|
||||
if (this->type == CONFIGVALUETYPESTRING) throw ((string)"Attempted to get param from value, which is not a set");
|
||||
set<ConfigParam>::iterator p = this->pval.find((ConfigParam)(pname));
|
||||
if (p == pval.end()) return "";
|
||||
return p->GetSingleVal();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user