Skeleton of application, at least compiles

This commit is contained in:
ftc
2020-11-24 22:48:54 +03:00
parent 988adcec4a
commit bd5a629fde
24 changed files with 1788 additions and 3 deletions
+463
View File
@@ -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();
}