diff --git a/src/parser.cpp b/src/parser.cpp index bda65d4..e724163 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -49,7 +49,7 @@ namespace /* internal */ LAST // ^ }; - struct token + struct token /* todo: string_view? */ { token(const char* b, const char* e); explicit token(TokenType t) @@ -69,6 +69,7 @@ namespace /* internal */ using TokenIt = vector::const_iterator; /* internal data */ + const char* cur_file; unsigned line = 1; pair prev_parents(Nobody, Nobody); Tree* cur_tree; @@ -87,6 +88,7 @@ namespace /* internal */ U32 sameNamedIndex(TokenIt& it); Sex parseSex(TokenIt& it, TokenIt end); I32 processDatePart(const char*& it_date, const char* end_date); + person_id parsePerson(TokenIt& it, TokenIt end); pair processParents(TokenIt& it, TokenIt end); vector parseSpouses(TokenIt& it, TokenIt end); /* return the spouse IDs */ @@ -97,6 +99,12 @@ namespace /* internal */ /* errors */ + void report_err() + { + endwin(); + cerr << "In file " << cur_file << " Line " << line << ": "; + } + [[noreturn]]void err_unexpected_end() { endwin(); @@ -106,15 +114,15 @@ namespace /* internal */ [[noreturn]]void err_unexpected_char(char c) { - endwin(); - cerr << "Line " << line << ": Unexpected '" << c << "'\n"; + report_err(); + cerr << "Unexpected '" << c << "'\n"; exit(1); } [[noreturn]]void err_expected(const char* what, token instead) { - endwin(); - cerr << "Line " << line << ": Expected " << what << " got "; + report_err(); + cerr << "Expected " << what << " got "; cerr.write(instead.begin, instead.size()); cerr << " instead\n"; exit(1); @@ -123,8 +131,8 @@ namespace /* internal */ [[noreturn]]void err_unexpected_token(token t) { - endwin(); - cerr << "Line " << line << ": Unexpected token '"; + report_err(); + cerr << "Unexpected token '"; cerr.write(t.begin, t.size()); cerr << "'\n"; exit(1); @@ -132,8 +140,8 @@ namespace /* internal */ [[noreturn]]void err_illegal_attribute(token attr) { - endwin(); - cerr << "Line " << line << ": Illegal attribute '"; + report_err(); + cerr << "Illegal attribute '"; cerr.write(attr.begin, attr.size()); cerr << "'\n"; exit(1); @@ -149,26 +157,34 @@ namespace /* internal */ [[noreturn]]void err_personUndefined(const string& name) { - endwin(); - cerr << "Line " << line << ": '" << name << "' is undefined.\n" + report_err(); + cerr << "'" << name << "' is undefined.\n" "Members need to be defined before they are used as relations\n"; exit(1); } [[noreturn]]void err_indexExcedes(U32 index, U32 max) { - endwin(); - cerr << "Line " << line << ": '" << index << "' is too large.\n" + report_err(); + cerr << "'" << 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'; exit(1); } + [[noreturn]]void err_no_previous(token t, U32 requested, U32 actual) + { + report_err(); + /* todo: print t */ + cerr << requested << "th last person requested but only " << actual << " are defined so far\n"; + exit(1); + } + [[noreturn]]void err_same_sex_spouse(const string& name, const Person& spouse) { - endwin(); - cerr << "Line " << line << ": '" << name << "' and their spouse " << spouse.name + report_err(); + cerr << "'" << name << "' and their spouse " << spouse.name << " are both " << (spouse.sex == M ? "male" : "female") << '\n'; exit(1); } @@ -199,9 +215,16 @@ namespace /* internal */ } } - if(type == STRING) + if(type != NUMBER) { - if(t.size() == 1) + if(t.begin[0] == '^') + { + type = LAST; + for(const char* p = t.begin; p < t.end; ++p) + if(*p != '^') + err_unexpected_char(*p); + } + else if(t.size() == 1) { switch(t.begin[0]) { @@ -211,9 +234,6 @@ namespace /* internal */ case '-': type = REPEAT; break; - case '^': - type = LAST; - break; case '\n': type = NEW_LINE; } @@ -439,6 +459,27 @@ namespace /* internal */ } + person_id parsePerson(TokenIt& it, TokenIt end) + { + person_id person; + if(it->type == LAST) + { + if(it->size() > cur_tree->size()) + err_no_previous(*it, it->size(), cur_tree->size()); + + person = cur_tree->last() - (it->size() - 1); + + ++it; + } + else + { + string name = parseName(it, end); + U32 same_name_index = sameNamedIndex(it); + person = findPerson(name, same_name_index); + } + return person; + } + pair processParents(TokenIt& it, TokenIt end) { ++it; /* skip attribute */ @@ -449,23 +490,13 @@ namespace /* internal */ parents = prev_parents; ++it; } - else if(it->type == LAST) - { - parents.first = cur_tree->last(); - prev_parents = parents; - ++it; - } else { - string name = parseName(it, end); - U32 same_name_index = sameNamedIndex(it); - parents.first = findPerson(name, same_name_index); + parents.first = parsePerson(it, end); if(it->type == SEPARATOR) { ++it; - string name = parseName(it, end); - U32 same_name_index = sameNamedIndex(it); - parents.second = findPerson(name, same_name_index); + parents.second = parsePerson(it, end); } prev_parents = parents; } @@ -482,21 +513,13 @@ namespace /* internal */ { switch(it->type) { + case LAST: 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; + spouses.push_back(parsePerson(it, end)); + break; case SEPARATOR: ++it; break; - case REPEAT: - case LAST: - ++it; - spouses.push_back( cur_tree->last() ); - break; case NEW_LINE: ++line; ++it; @@ -547,7 +570,7 @@ namespace /* internal */ Sex sex = parseSex(it, end); - while(true) + while(true) /* Parse person attributes */ { switch(it->type) { @@ -647,10 +670,13 @@ Tree parseTftFile(const char* path) Tree result(path); cur_tree = &result; + cur_file = path; MappedFile mapedfile = mapfile(file); + vector tokens = tokenize(mapedfile); + // Parse the tokens state_neutral(tokens); return result; diff --git a/src/person.cpp b/src/person.cpp index 5fe1987..f10618f 100644 --- a/src/person.cpp +++ b/src/person.cpp @@ -170,7 +170,7 @@ U16 person_box_height(ZoomLevel z) case ZoomLevel::ALL_NAMES: case ZoomLevel::FIRST_NAME: return 5; - case NO_NAME: + case ZoomLevel::NO_NAME: return 2; } } diff --git a/src/tft.cpp b/src/tft.cpp index f50e5de..670704a 100644 --- a/src/tft.cpp +++ b/src/tft.cpp @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) init_ncurses(); - if( !tree.isEmpty() ) + if( !tree.empty() ) displayTree(tree); return 0; diff --git a/src/tree.cpp b/src/tree.cpp index aa70252..59e9585 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -98,7 +98,12 @@ Tree& Tree::operator-= (const Tree& other) return *this; } -bool Tree::isEmpty() +U32 Tree::size() +{ + return people_.size(); +} + +bool Tree::empty() { return people_.empty(); } diff --git a/src/tree.hpp b/src/tree.hpp index 322adf1..dc056a3 100644 --- a/src/tree.hpp +++ b/src/tree.hpp @@ -40,7 +40,8 @@ public: Tree& operator+= (const Tree& other); Tree& operator-= (const Tree& other); - bool isEmpty(); + U32 size(); + bool empty(); person_id last(); // Get person by id @@ -61,7 +62,8 @@ public: std::vector findChildren(person_id person, person_id exclude) const; // exclude the child with the given ID void addPerson(const char* name, Sex sex, person_id father=Nobody, person_id mother=Nobody, EventTime birth={}, EventTime death={}); - bool addRelation(person_id first, RelType, person_id second, bool update=true); //0 - failure 1 - success + // Return weather it succeeded or not + bool addRelation(person_id first, RelType, person_id second, bool update=true); void removePerson(person_id id); //Removes a member and the last one takes his ID void removeRelation(person_id first, person_id second); diff --git a/todo.txt b/todo.txt index 9cab631..3f2bead 100644 --- a/todo.txt +++ b/todo.txt @@ -11,28 +11,24 @@ man page types - screenCoord... tft descriton - multiple trees, why its better, features, ^ -tft ^ should expand to last person (for parents it doesnt) tft merge trees relation Visualization: - more granular zooming - snap to selection/scroll with selection +- centralize when changing focus - horizontal navigation between cousins - color on different consoles - screen resize - empty tree - half children and main tree -- distinct tree regions - aproximate birth day -- indicator for people with their own tree -- centralize when changing focus Compilation - static and dynamic ncurses ? - info bar -- tree visualizer component - distinct regions of a tree - same person can be displayed twice - coordinate types for large trees