Files
tft/src/parser.cpp
T

387 lines
7.2 KiB
C++

#include <iostream>
/* #include <string_view> */
#include <string>
#include "tree.hpp"
#include "utils.hpp"
/* macro rather then function to avoid warnings */
#define UNEXPECTED_END() endwin(); cerr << "Unexpected end of file" << std::endl; exit(1)
using std::cerr;
/* using std::string_view; */
using std::string;
using std::vector;
using std::pair;
namespace /* internal */
{
struct alloced
{
char* const data;
char* const end;
alloced(U32 len)
: data(new char[len])
, end(data + len)
{
}
~alloced()
{
delete[] data;
}
};
char* end_data; /* past the end pointer */
unsigned line = 1;
pair<person_id, person_id> prev_parents(Nobody, Nobody);
/* state */
void state_neutral(char* it_data);
void state_person(char*& it_data);
/* void state_person_attribute() */
/* result */
Tree* cur_tree;
bool isNum(char c)
{
return c >= '0' && c <= '9';
}
void unexpected_char(char c)
{
endwin();
cerr << "Line " << line << ": Unexpected '" << c << std::endl;
exit(1);
}
/* void unexpected_end() */
/* { */
/* endwin(); */
/* cerr << "Unexpected end of file" << std::endl; */
/* exit(1); */
/* } */
void proceedToNameEnd(char*& it_data)
{
while(it_data < end_data &&
*it_data != ',' &&
*it_data != '\n' &&
!isNum(*it_data))
{
if(*it_data == ':' /* && *it_data == '-' */)
unexpected_char(*it_data);
++it_data;
}
}
U32 sameNamedIndex(char*& it_data)
{
U32 res = 0;
if(isNum(*it_data))
{
res = *it_data - '0'; /* todo many digits */
++it_data;
}
return res;
}
Sex findSex(char*& it_data)
{
Sex res = M;
if(*it_data == ',')
{
++it_data;
if(*it_data == 'M')
res = M;
else if(*it_data == 'F')
res = F;
else
unexpected_char(*it_data);
++it_data;
if(*it_data != '\n')
unexpected_char(*it_data);
}
return res;
}
I32 processDatePart(char*& it_data)
{
I32 sign = 1;
I32 val = 0;
for(; it_data < end_data; ++it_data)
{
switch(*it_data)
{
case '-':
sign = -1;
break;
case '0' ... '9':
val = val*10 + (*it_data - '0');
break;
case '.':
if(val == 0)
unexpected_char('.');
else
return sign * val;
break;
case '\n':
++line;
return sign * val;
/* case '?': todo */
default:
unexpected_char(*it_data);
}
}
UNEXPECTED_END();
}
EventTime processDate(char*& it_data) /* todo validate */
{
EventTime date;
date.year = processDatePart(it_data);
if(*it_data == '.')
{
++it_data;
date.month = processDatePart(it_data);
if(*it_data == '.')
{
++it_data;
date.day = processDatePart(it_data);
}
}
return date;
}
pair<person_id, person_id> processParents(char*& it_data)
{
pair<person_id, person_id> res(Nobody, Nobody);
U32 same_name_index = 0;
string name; /* todo dont use string */
for(; it_data < end_data; ++it_data)
{
switch(*it_data)
{
case ',':
if(res.first == Nobody)
res.first = cur_tree->findId(name.data())[same_name_index]; /* todo validate index */
else if(res.second == Nobody)
res.second = cur_tree->findId(name.data())[same_name_index];
else
/* todo */TFT_EXIT(1);
same_name_index = 0;
name.clear();
break;
case '-':
if(name.empty() && res.first == Nobody)
return prev_parents;
else
unexpected_char('-');
case '\n':
++line;
if(res.first == Nobody)
res.first = cur_tree->findId(name.data())[same_name_index];
else
res.second = cur_tree->findId(name.data())[same_name_index];
return prev_parents = res;
case '_':
name.push_back(' ');
break;
case '0' ... '9':
same_name_index = *it_data - '0';
break;
default:
name.push_back(*it_data);
}
}
UNEXPECTED_END();
}
vector<person_id> parseSpouses(char*& it_data)
{
vector<person_id> res;
U32 same_name_index = 0;
string name;
for(; it_data < end_data; ++it_data)
{
switch(*it_data)
{
case '-':
if(name.empty())
res.push_back( cur_tree->last() );
else
unexpected_char('-');
break;
case ',':
if(!name.empty())
{
res.push_back( cur_tree->findId(name.data())[same_name_index] );
name.clear();
same_name_index = 0;
}
else
unexpected_char(',');
break;
case '\n':
++line;
if(!name.empty())
res.push_back( cur_tree->findId(name.data())[same_name_index] );
return res;
case '_':
name.push_back(' ');
break;
case '0' ... '9':
same_name_index = *it_data - '0';
break;
default:
name.push_back(*it_data);
}
}
if(!name.empty())
res.push_back( cur_tree->findId(name.data())[same_name_index] );
return res;
}
void state_neutral(char* it_data)
{
for(; it_data < end_data; ++it_data)
{
switch(*it_data)
{
default:
state_person(it_data);
break;
case '\n':
++line;
break;
case ',':
case '-':
case 0 ... 9:
case ':':
unexpected_char(*it_data);
}
}
}
void state_person(char*& it_data)
{
char* name_start = it_data;
pair<person_id, person_id> parents(Nobody, Nobody);
vector<person_id> spouses;
EventTime birth{};
EventTime death{};
proceedToNameEnd(it_data); /* todo merge funcs */
string name(name_start, it_data);
U32 same_name_index = sameNamedIndex(it_data);
/* string_view name(name_start, it_data); */
for(char& c: name)
{
if(c == '_')
c = ' ';
}
Sex sex = findSex(it_data);
for(; it_data < end_data; ++it_data)
{
switch(*it_data)
{
case '\n':
++line;
break;
default:
if(it_data[1] == ':') /* todo */
{
switch(*it_data)
{
case 'b':
it_data += 2;
birth = processDate(it_data);
break;
case 'd':
it_data += 2;
death = processDate(it_data);
break;
case 'p':
it_data += 2;
parents = processParents(it_data);
break;
case 's':
it_data += 2;
spouses = parseSpouses(it_data);
break;
default:
unexpected_char(it_data[-1]);
}
}
else
{
cur_tree->addPerson(name.c_str(), sex, parents.first, parents.second, birth, death);
for(person_id spouse: spouses)
cur_tree->addRelation(spouse, Spouse, cur_tree->last());
--it_data; /* rewind 1 */
return;
}
break;
/* case 0...9: */
/* case ':': */
/* unexpected_char(*it_data); */
/* default: */
/* break; */
}
}
cur_tree->addPerson(name.c_str(), sex, parents.first, parents.second, birth, death);
for(person_id spouse: spouses)
cur_tree->addRelation(spouse, Spouse, cur_tree->last());
}
alloced preprocess(MappedFile file) /* remove whitespace and comments */
{
alloced res(file.len);
char* itres = res.data;
char* end = file.data + file.len;
for(char* it = file.data; it < end; ++it)
{
switch(*it)
{
case '#':
while(++it != end && *it != '\n'); /* skip comment */
*itres++ = '\n'; /* add \n */
break;
case ' ':
case '\t':
case '\r':
break;
default:
*itres = *it;
++itres;
}
}
end_data = itres;
return res;
}
}
Tree parseTftFile(fd_t file)
{
Tree result("t");
cur_tree = &result;
MappedFile mapedfile = mapfile(file);
alloced preprocessed = preprocess(mapedfile);
state_neutral(preprocessed.data);
return result;
}