class node{public: class iterator { private: std::list::iterator it; public: typedef std::list::iterator::difference_type difference_type; typedef std::list::iterator::iterator_category iterator_category; typedef node* pointer; typedef node& reference; typedef node value_type; explicit iterator(std::list::iterator i) : it(i) { } bool operator==(const iterator &i) const { return it==i.it; } bool operator!=(const iterator &i) const { return it!=i.it; } pointer operator->(){ return *it; } reference operator*(){ return *(*it); } iterator &operator++(){ ++it; return *this; } iterator operator++(int){ iterator i(*this); ++it; return i; } iterator &operator--(){ --it; return *this; } iterator operator--(int){ iterator i(*this); --it; return i; } }; // rest of node class snipped, including const_iterator version of above};
So now the xcs::node class contains a std::list to avoid yesterday's problems, and the node class typedefs iterator_base as node::iterator.
So the node's begin() and end() methods return the above iterator, constructed simply from the node's std::list<>'s begin() and end() methods.
So you are back to:
for(xcs::node::iterator i=n.begin();i!=n.end();++i) std::cout << i->name << "\n";
I've also implemeneted an include system in the xcs files so that you can include one xcs file in another.
Another major improvement is the abolishment of a seperate xcs::tree class. Now, you just do:
xcs::node root;xcs::open("sample.xcs",root);
You can even open a file into a node of an existing xcs tree. Support has also been added for manually building trees in code with node::add() and node::remove() methods.
In case it is of interest, here's the complete xcs library:
xcs.h
#ifndef xcs_H#define xcs_H#include #include #include #include namespace xcs{class exception{protected: std::string text;public: exception(const std::string &s) : text(s) { } std::string parameter() const { return text; } virtual std::string what() const=0;};class bad_name : public exception{public: bad_name(const std::string &s) : exception(s) { } virtual std::string what() const { return "Name not found - "+text; }};class bad_conversion : public exception{public: bad_conversion(const std::string &s) : exception(s) { } virtual std::string what() const { return "Invalid conversion - "+text; }};class bad_path : public exception{public: bad_path(const std::string &s) : exception(s) { } virtual std::string what() const { return "Unable to open - "+text; }};class bad_syntax : public exception{private: std::string path; size_t line;public: bad_syntax(const std::string &p,size_t n,const std::string &s) : exception(s),path(p),line(n) { } virtual std::string what() const;};class node{public: class iterator { private: std::list::iterator it; public: typedef std::list::iterator::difference_type difference_type; typedef std::list::iterator::iterator_category iterator_category; typedef node* pointer; typedef node& reference; typedef node value_type; explicit iterator(std::list::iterator i) : it(i) { } bool operator==(const iterator &i) const { return it==i.it; } bool operator!=(const iterator &i) const { return it!=i.it; } pointer operator->(){ return *it; } reference operator*(){ return *(*it); } iterator &operator++(){ ++it; return *this; } iterator operator++(int){ iterator i(*this); ++it; return i; } iterator &operator--(){ --it; return *this; } iterator operator--(int){ iterator i(*this); --it; return i; } }; class const_iterator { private: std::list::const_iterator it; public: typedef std::list::const_iterator::difference_type difference_type; typedef std::list::const_iterator::iterator_category iterator_category; typedef const node* pointer; typedef const node& reference; typedef node value_type; explicit const_iterator(std::list::const_iterator i) : it(i) { } bool operator==(const const_iterator &i) const { return it==i.it; } bool operator!=(const const_iterator &i) const { return it!=i.it; } pointer operator->(){ return *it; } reference operator*(){ return *(*it); } const_iterator &operator++(){ ++it; return *this; } const_iterator operator++(int){ const_iterator i(*this); ++it; return i; } const_iterator &operator--(){ --it; return *this; } const_iterator operator--(int){ const_iterator i(*this); --it; return i; } };private: node *pa; std::list ch;public: node(const std::string &n="",const std::string &v="") : name(n),value(v),pa(0) { } ~node(); node &add(const std::string &n,const std::string &v=""); void remove(const std::string &n); bool contains(const std::string &n) const; node &operator[](const std::string &n); const node &operator[](const std::string &n) const; iterator begin(){ return iterator(ch.begin()); } iterator end(){ return iterator(ch.end()); } const_iterator begin() const { return const_iterator(ch.begin()); } const_iterator end() const { return const_iterator(ch.end()); } bool is_root() const { return(pa ? false : true); } std::string full_name() const; template<class T> bool is() const; template<> bool is() const { return true; } template<> bool is<char*>() const { return false; } template<> bool is<const char*>() const { return false; } template<class T> T as() const; template<> std::string as() const { return value; } template<class T> void assign(const T &t){ std::stringstream s; s << t; value=s.str(); } std::string name,value;};template<class T> bool node::is() const{ std::stringstream ss(value); T t; ss >> t; if(ss.fail()) return false; std::string s; ss >> s; return ss.fail();}template<class T> T node::as() const{ if(!is()) throw bad_conversion(value); std::stringstream ss(value); T t=T(); ss >> t; return t;}void open(const std::string &path,node &root);}#endif
xcs.cpp
#include "xcs.h"#include #include #include std::string xcs::bad_syntax::what() const{ std::ostringstream os; os << "Invalid syntax in " << path << " at line " << line << " - " << text; return os.str();}xcs::node::~node(){ for(std::list::iterator i=ch.begin();i!=ch.end();++i) delete *i;}xcs::node &xcs::node::add(const std::string &n,const std::string &v){ node *a=new node(n,v); a->pa=this; ch.push_back(a); return *a;}void xcs::node::remove(const std::string &n){ std::list::iterator i=ch.begin(); while(i!=ch.end()) { if((*i)->name==n){ delete (*i); i=ch.erase(i); } else ++i; }}bool xcs::node::contains(const std::string &n) const{ for(std::list::const_iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return true; return false;}xcs::node &xcs::node::operator[](const std::string &n){ for(std::list::iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return *(*i); throw bad_name(std::string(pa ? full_name()+"." : "")+n);}const xcs::node &xcs::node::operator[](const std::string &n) const{ for(std::list::const_iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return *(*i); throw bad_name(std::string(pa ? full_name()+"." : "")+n);}std::string xcs::node::full_name() const{ std::stack st; const node *p=pa; while(p && p->pa) { st.push(p->name); p=p->pa; } std::string r; while(!st.empty()) { r+=st.top()+"."; st.pop(); } return r+name;}namespace{bool is_absolute_path(const std::string &s){ if(s.length()>0) { if(s[0]=='\\' || s[0]=='/') return true; } if(s.length()>3) { if(s.substr(1,2)==":\\" || s.substr(1,2)==":/") return true; } return false;}class source{private: std::ifstream is; int last;public: source(const std::string &p) : path(p),line(1) { is.open(path.c_str()); } bool fail() const { return is.fail(); } int get(){ int c=is.get(); if(c=='\n') ++line; return last=c; } void unget(){ is.unget(); if(last=='\n') --line; } std::string path; size_t line;};inline std::string ch_to_str(char c){ std::ostringstream os; os << c; return os.str(); }class scanner{public: enum types { eof,id,lb,rb,assign,semi,rwinclude };private: int skip(); types strlit(); types next(); std::stack src;public: scanner(){ } ~scanner(){ while(!src.empty()) pop(); } void push(const std::string &path); void pop(){ if(!src.empty()){ delete src.top(); src.pop(); } } scanner &operator()(bool get){ if(get) type=next(); return *this; } scanner &operator()(types t,bool get){ (*this)(get); if(t!=type) error(text); return *this; } void error(const std::string &s){ throw xcs::bad_syntax(src.top()->path,src.top()->line,s); } types type; std::string text; std::string rootdir;};int scanner::skip(){ int c=src.top()->get(); while(std::isspace(c)) c=src.top()->get(); return c;}scanner::types scanner::strlit(){ int c=src.top()->get(); while(c!='\"') { if(c=='\n' || c==EOF) error("non-terminated string constant"); if(c=='\\') { c=src.top()->get(); switch(c) { case 'n': c='\n'; break; case 't': c='\t'; break; } } text+=char(c); c=src.top()->get(); } c=skip(); if(c=='\"') return strlit(); else { src.top()->unget(); return id; }}scanner::types scanner::next(){ text=""; int c=skip(); if(c==EOF){ text="EOF"; return eof; } if(c=='{'){ text+=char(c); return lb; } if(c=='}'){ text+=char(c); return rb; } if(c=='='){ text+=char(c); return assign; } if(c==';'){ text+=char(c); return semi; } if(c=='\"') return strlit(); if(std::isalnum(c)) { while(std::isalnum(c) || c=='_'){ text+=char(c); c=src.top()->get(); } src.top()->unget(); if(text=="include") return rwinclude; return id; } error(ch_to_str(c)); return eof;}void scanner::push(const std::string &path){ source *s=new source(path); if(s->fail()){ delete s; throw xcs::bad_path(path); } src.push(s);}void block_construct(scanner &sc,xcs::node &root,bool get);void construct(scanner &sc,xcs::node &root,bool get);void load_from_file(scanner &sc,xcs::node &root,const std::string &path);void id_construct(scanner &sc,xcs::node &root,bool get){ sc(sc.id,get); std::string name=sc.text,value=sc.text; sc(true); if(sc.type==sc.assign) { sc(sc.id,true); value=sc.text; sc(true); } if(root.contains(name)) sc.error(name+" duplicated in scope"); xcs::node &n=root.add(name,value); switch(sc.type) { case sc.lb : block_construct(sc,n,false); break; case sc.semi: sc(true); break; default: sc.error(sc.text); }}void block_construct(scanner &sc,xcs::node &root,bool get){ sc(sc.lb,get)(true); while(sc.type!=sc.rb) construct(sc,root,false); sc(true);}void include_construct(scanner &sc,xcs::node &root,bool get){ sc(sc.rwinclude,get)(sc.id,true); std::string path=sc.text; sc(sc.semi,true); if(!is_absolute_path(path)) path=sc.rootdir+path; load_from_file(sc,root,path); sc(true);}void construct(scanner &sc,xcs::node &root,bool get){ sc(get); switch(sc.type) { case sc.rwinclude: include_construct(sc,root,false); break; case sc.id : id_construct(sc,root,false); break; default: sc.error(sc.text); }}void load_from_file(scanner &sc,xcs::node &root,const std::string &path){ sc.push(path); sc(true); while(sc.type!=sc.eof) construct(sc,root,false); sc.pop();}}void xcs::open(const std::string &path,node &root){ scanner sc; if(is_absolute_path(path)) sc.rootdir=path.substr(0,path.find_last_of("\\/"))+"\\"; load_from_file(sc,root,path);}