#include #include #include #include "tree.hpp" #include "utils.hpp" #include using std::cerr; using std::string_view; using std::string; using std::vector; using std::pair; /* using std::endl; */ namespace /* internal */ { struct alloced { char* const data; char* const end; alloced(U32 len) : data(new char[len]) , end(data + len) {} alloced(const alloced&) = delete; alloced(alloced&& other) : data(other.data) , end(other.end) { const_cast(other.data) = nullptr; } ~alloced() { delete[] data; } }; enum TokenType { ATTRIBUTE, STRING, /* DATE, */ NUMBER, NEW_LINE, SEPARATOR, /* , */ REPEAT /* - */ }; struct token { token(const char* b, const char* e); explicit token(TokenType t) : type(t) { } U32 size() const { return end - begin; } const char* begin; const char* end; TokenType type; }; using TokenIt = vector::const_iterator; char* end_data_; /* past the end pointer */ unsigned line = 1; pair prev_parents(Nobody, Nobody); /* state */ /* void state_neutral(char* it_data); */ void state_person(TokenIt& it, TokenIt end); /* void state_person_attribute() */ /* result */ Tree* cur_tree; [[noreturn]]void err_unexpected_end() { endwin(); cerr << "Unexpected end of file\n"; cerr.flush(); exit(1); } [[noreturn]]void err_unexpected_char(char c) { endwin(); cerr << "Line " << line << ": Unexpected '" << c << "'\n"; cerr.flush(); exit(1); } [[noreturn]]void err_expected(const char* what, token instead) { endwin(); cerr << "Line " << line << ": Expected " << what << "got"; cerr.write(instead.begin, instead.size()); cerr << "instead\n"; cerr.flush(); exit(1); } [[noreturn]]void err_unexpected_token(token t) { endwin(); cerr << "Line " << line << ": Unexpected token '"; cerr.write(t.begin, t.size()); cerr << "'\n"; cerr.flush(); exit(1); } [[noreturn]]void err_illegal_attribute(token attr) { endwin(); cerr << "Line " << line << ": Illegal attribute '"; cerr.write(attr.begin, attr.size()); cerr << "'\n"; cerr.flush(); exit(1); } /* [[noreturn]]void err_thirdParent(const string& extra_name) */ /* { */ /* endwin(); */ /* cerr << "Line " << line << ": two parents are already defined, " */ /* << extra_name << " is third\n"; */ /* cerr.flush(); */ /* exit(1); */ /* } */ [[noreturn]]void err_personUndefined(const string& name) { endwin(); cerr << "Line " << line << ": '" << name << "' is undefined.\n" "Members need to be defined before they are used as relations\n"; cerr.flush(); exit(1); } [[noreturn]]void err_indexExcedes(U32 index, U32 max) { endwin(); cerr << "Line " << line << ": '" << index << "' is too large.\n" << "At most the index after a name can be the number of people with the " "same name already defined - 1, since the count starts from 0.\n" "In this case the max is " << max << '\n'; cerr.flush(); exit(1); } /* void err_name_index(const string& name, U32 i) */ /* { */ /* endwin(); */ /* cerr << "Line " << line << " the number given after the declaration of a person can" */ /* " only be his place among the same named. " << i << " doesn't satisfy this for '" */ /* << name << '\'' << endl; */ /* exit(1); */ /* } */ bool isNum(char c) { return c >= '0' && c <= '9'; } TokenType findType(token t) { TokenType type = NUMBER; for(const char* it = t.begin; it < t.end; ++it) { if(!isNum(*it)) { type = STRING; break; } } if(type == STRING) { if(t.size() == 1) { switch(t.begin[0]) { case ',': type = SEPARATOR; break; case '-': type = REPEAT; break; case '\n': type = NEW_LINE; } } else if(t.size() == 2) { if(t.begin[1] == ':') type = ATTRIBUTE; } } return type; } token::token(const char* b, const char* e) : begin(b) , end(e) { type = findType(*this); } U32 calcNumber(token t) { assert(t.type == NUMBER); U32 val = 0; for(const char* it = t.begin; it < t.end; ++it) val = val*10 + *it-'0'; return val; } void expect_newline(TokenIt& it) { if(it->type == NEW_LINE) { ++line; ++it; } else err_unexpected_token(*it); } /* construct name from tokens */ string parseName(TokenIt& it, TokenIt end) { if(it->type != STRING) err_expected("name", *it); string name; for(; it < end && it->type == STRING; ++it) { name.append(it->begin, it->end); name += ' '; } name.pop_back(); /* remove last ' ' */ return name; } /* find a person's id in the tree based on his name and index * among same named */ person_id findPerson(const string& name, U32 same_name_index) { vector same_named = cur_tree->findId(name); if(same_named.empty()) err_personUndefined(name); else if(same_name_index >= same_named.size()) err_indexExcedes(same_name_index, same_named.size()-1); else return same_named[same_name_index]; } U32 sameNamedIndex(TokenIt& it) { U32 res = 0; if(it->type == NUMBER) { res = calcNumber(*it); ++it; } return res; } Sex parseSex(TokenIt& it, TokenIt end) { Sex res = M; if(it->type == SEPARATOR) { ++it; if(it == end) err_unexpected_end(); if(it->type == STRING) { if(it->size() == 1) { if(it->begin[0] == 'M') res = M; else if(it->begin[0] == 'F') res = F; else err_expected("sex", *it); ++it; } else err_expected("sex", *it); } else err_unexpected_token(*it); } expect_newline(it); return res; } I32 processDatePart(const char*& it_date, const char* end_date) { I32 sign = 1; I32 val = 0; for(; it_date < end_date; ++it_date) { switch(*it_date) { case '-': sign = -1; break; case '0' ... '9': val = val*10 + (*it_date - '0'); break; case '.': if(val == 0) err_unexpected_char('.'); else return sign * val; break; /* case '?': todo */ default: err_unexpected_char(*it_date); } } return sign * val; } EventTime state_date(TokenIt& it) /* todo validate */ { ++it; /* skip attribute */ if(it->type == STRING || it->type == NUMBER) { EventTime date; const char* it_date = it->begin; const char* end_date = it->end; date.year = processDatePart(it_date, end_date); if(*it_date == '.') { ++it_date; date.month = processDatePart(it_date, end_date); if(*it_date == '.') { ++it_date; date.day = processDatePart(it_date, end_date); } } ++it; expect_newline(it); return date; } else err_expected("date", *it); } pair processParents(TokenIt& it, TokenIt end) { ++it; /* skip attribute */ pair parents(Nobody, Nobody); if(it->type == REPEAT) { parents = prev_parents; ++it; } else { string name = parseName(it, end); U32 same_name_index = sameNamedIndex(it); parents.first = findPerson(name, same_name_index); if(it->type == SEPARATOR) { ++it; string name = parseName(it, end); U32 same_name_index = sameNamedIndex(it); parents.second = findPerson(name, same_name_index); } prev_parents = parents; } expect_newline(it); return parents; } vector parseSpouses(TokenIt& it, TokenIt end) { ++it; /* skip attribute */ vector spouses; while(true) { switch(it->type) { case STRING: { string name = parseName(it, end); /* todo unify in 1 function */ U32 same_name_index = sameNamedIndex(it); spouses.push_back(findPerson(name, same_name_index)); } break; case SEPARATOR: ++it; break; case REPEAT: ++it; spouses.push_back( cur_tree->last() ); break; case NEW_LINE: ++line; ++it; return spouses; default: err_unexpected_token(*it); } } } void state_neutral(const vector& tokens) { auto end = tokens.cend(); for(TokenIt it = tokens.cbegin(); it < end;) { switch(it->type) { case NEW_LINE: ++line; ++it; break; case STRING: state_person(it, end); break; default: err_unexpected_token(*it); } } } void state_person(TokenIt& it, TokenIt end) { pair parents(Nobody, Nobody); vector spouses; EventTime birth{}; EventTime death{}; string name = parseName(it, end); if(it->type == NUMBER) ++it; /* U32 same_name_index = sameNamedIndex(it); */ /* todo */ /* if(cur_tree->findId(name).size() != same_name_index) */ /* err_name_index(name, same_name_index); */ Sex sex = parseSex(it, end); while(true) { switch(it->type) { case ATTRIBUTE: switch(it->begin[0]) { case 'b': birth = state_date(it); /* todo check for redefinition */ break; case 'd': death = state_date(it); break; case 'p': parents = processParents(it, end); break; case 's': spouses = parseSpouses(it, end); break; default: err_illegal_attribute(*it); } break; case NEW_LINE: ++line; ++it; case STRING: /* todo str prevent copy */ 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()); return; default: err_unexpected_token(*it); } } } alloced removeComments(MappedFile file) /* todo - drop */ { alloced res(file.len); char* itres = res.data; char* end = file.data + file.len; for(char* it = file.data; it < end; ++it) { if(*it == '#') { while(++it != end && *it != '\n'); /* skip comment */ *itres++ = '\n'; /* add \n */ } else { *itres = *it; ++itres; } } end_data_ = itres; return res; } vector tokenize(const char* data_begin, const char* data_end) { vector tokens; tokens.reserve(256); const char* token_begin = data_begin; for(const char* it = data_begin; it < data_end; ++it) { switch(*it) { case ':': if(token_begin < it) tokens.emplace_back(token_begin, it+1); /* consume the : */ token_begin = it + 1; break; case ' ': case '\t': if(token_begin < it) tokens.emplace_back(token_begin, it); token_begin = it + 1; /* skip the ws */ break; case ',': case '\n': if(token_begin < it) tokens.emplace_back(token_begin, it); tokens.emplace_back(it, it+1); token_begin = it+1; break; /* case '0' ... '9': */ default: break; } } tokens.emplace_back(NEW_LINE); tokens.emplace_back(NEW_LINE); return tokens; } } Tree parseTftFile(const char* path) { fd_t file = openOrDie(path); Tree result(path); cur_tree = &result; MappedFile mapedfile = mapfile(file); alloced preprocessed = removeComments(mapedfile); vector tokens = tokenize(preprocessed.data, end_data_); state_neutral(tokens); return result; }