Browse Source

create_from_string now throws a ParseError exception instead of returning an empty shared pointer
keep track of position when parsing a string, for better error feedback, for example when using files


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jelte-datadef@290 e5f2f494-b856-4b98-b285-d166d9295462

Jelte Jansen 15 years ago
parent
commit
6d17271322
2 changed files with 113 additions and 51 deletions
  1. 98 50
      src/lib/cc/cpp/data.cc
  2. 15 1
      src/lib/cc/cpp/data.h

+ 98 - 50
src/lib/cc/cpp/data.cc

@@ -27,6 +27,13 @@ std::ostream& operator <<(std::ostream &out, const ISC::Data::ElementPtr& e) {
     return out << e->str();
 }
 
+const char*
+ParseError::what() const throw() {
+    stringstream ss;
+    ss << msg << " line " << line << " pos " << pos;
+    return ss.str().c_str();
+}
+
 //
 // factory functions
 //
@@ -64,7 +71,6 @@ ElementPtr
 Element::create(const bool b)
 {
     try {
-        cout << "creating boolelement" << endl;
         return ElementPtr(new BoolElement(b));
     } catch (std::bad_alloc) {
         return ElementPtr();
@@ -109,83 +115,113 @@ char_in(char c, const char *chars)
 }
 
 static void
-skip_chars(std::stringstream &in, const char *chars)
+skip_chars(std::istream &in, const char *chars, int& line, int& pos)
 {
     char c = in.peek();
     while (char_in(c, chars) && c != EOF) {
+        if (c == '\n') {
+            line++;
+            pos = 1;
+        } else {
+            pos++;
+        }
         in.get();
         c = in.peek();
     }
 }
+/*static void
+skip_chars(std::istream &in, const char *chars)
+{
+    int l = 0, p = 0;
+    skip_chars(in, chars, l, p);
+}*/
 
 // skip on the input stream to one of the characters in chars
 // if another character is found this function returns false
 // unles that character is specified in the optional may_skip
 //
 // the character found is left on the stream
-static bool
-skip_to(std::stringstream &in, const char *chars, const char *may_skip="")
+static void
+skip_to(std::istream &in, int& line, int& pos, const char* chars, const char* may_skip="")
 {
     char c = in.get();
+    pos++;
     while (c != EOF) {
+        if (c == '\n') {
+            pos = 1;
+            line++;
+        }
         if (char_in(c, may_skip)) {
             c = in.get();
+            pos++;
         } else if (char_in(c, chars)) {
             while(char_in(in.peek(), may_skip)) {
+                if (in.peek() == '\n') {
+                    pos = 1;
+                    line++;
+                }
                 in.get();
+                pos++;
             }
             in.putback(c);
-            return true;
+            pos--;
+            return;
         } else {
-            // TODO: provide feeback mechanism?
-            cout << "error, '" << c << "' read; one of \"" << chars << "\" expected" << endl;
-            in.putback(c);
-            return false;
+            throw ParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", line, pos);
         }
     }
-    // TODO: provide feeback mechanism?
-    cout << "error, EOF read; one of \"" << chars << "\" expected" << endl;
-            in.putback(c);
-    return false;
+    throw ParseError(std::string("EOF read, one of \"") + chars + "\" expected", line, pos);
 }
 
+/*static bool
+skip_to(std::istream &in, const char *chars, const char *may_skip="") {
+    int line = 0, pos = 0;
+    return skip_to(in, line, pos, chars, may_skip);
+}*/
+
 static std::string
-str_from_stringstream(std::stringstream &in)
+str_from_stringstream(std::istream &in, int& line, int& pos) throw (ParseError)
 {
     char c = 0;
     std::stringstream ss;
     c = in.get();
+    pos++;
     if (c == '"') {
         c = in.get();
+        pos++;
     } else {
-        return "badstring";
+        throw ParseError("String expected", line, pos);
     }
     while (c != EOF && c != '"') {
         ss << c;
         if (c == '\\' && in.peek() == '"') {
             ss << in.get();
+            pos++;
         }
         c = in.get();
+        pos++;
     }
     return ss.str();
 }
 
 static std::string
-word_from_stringstream(std::stringstream &in)
+word_from_stringstream(std::istream &in, int& line, int& pos)
 {
     std::stringstream ss;
     while (isalpha(in.peek())) {
         ss << (char) in.get();
     }
+    pos += ss.str().size();
     return ss.str();
 }
 
 
 static ElementPtr
-from_stringstream_int_or_double(std::stringstream &in)
+from_stringstream_int_or_double(std::istream &in, int &line, int &pos)
 {
     int i;
     in >> i;
+    // TODO count pos
     if (in.peek() == '.') {
         double d;
         in >> d;
@@ -197,9 +233,9 @@ from_stringstream_int_or_double(std::stringstream &in)
 }
 
 static ElementPtr
-from_stringstream_bool(std::stringstream &in)
+from_stringstream_bool(std::istream &in, int& line, int& pos)
 {
-    std::string word = word_from_stringstream(in);
+    std::string word = word_from_stringstream(in, line, pos);
     if (boost::iequals(word, "True")) {
         return Element::create(true);
     } else if (boost::iequals(word, "False")) {
@@ -210,65 +246,78 @@ from_stringstream_bool(std::stringstream &in)
 }
 
 static ElementPtr
-from_stringstream_string(std::stringstream &in)
+from_stringstream_string(std::istream &in, int& line, int& pos)
 {
-    return Element::create(str_from_stringstream(in));
+    return Element::create(str_from_stringstream(in, line, pos));
 }
 
 static ElementPtr
-from_stringstream_list(std::stringstream &in)
+from_stringstream_list(std::istream &in, int& line, int& pos)
 {
     char c = 0;
     std::vector<ElementPtr> v;
     ElementPtr cur_list_element;
+    //cout << "reading list at line " << line << " pos " << pos << endl;
 
-    skip_chars(in, " \t\n");
+    skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != ']') {
-        cur_list_element = Element::create_from_string(in);
-        v.push_back(cur_list_element);
-        if (!skip_to(in, ",]", " \t\n")) {
-            return ElementPtr();
+        //cout << "at line " << line << " pos " << pos << " cur c: " << c << " next c: " << char(in.peek()) << endl;
+        if (in.peek() != ']') {
+            cur_list_element = Element::create_from_string(in, line, pos);
+            v.push_back(cur_list_element);
+            skip_to(in, line, pos, ",]", " \t\n");
         }
         c = in.get();
+        pos++;
     }
     return Element::create(v);
 }
 
 static ElementPtr
-from_stringstream_map(std::stringstream &in)
+from_stringstream_map(std::istream &in, int& line, int& pos)
 {
     char c = 0;
     std::map<std::string, ElementPtr> m;
     std::pair<std::string, ElementPtr> p;
     std::string cur_map_key;
     ElementPtr cur_map_element;
-    skip_chars(in, " \t\n");
+    skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != '}') {
-        p.first = str_from_stringstream(in);
-        if (!skip_to(in, ":", " \t\n")) {
-            return ElementPtr();
-        } else {
-            // skip the :
-            in.get();
-        }
-        p.second = Element::create_from_string(in);
+        p.first = str_from_stringstream(in, line, pos);
+        skip_to(in, line, pos, ":", " \t\n");
+        // skip the :
+        in.get();
+        pos++;
+        p.second = Element::create_from_string(in, line, pos);
         if (!p.second) { return ElementPtr(); };
         m.insert(p);
-        skip_to(in, ",}", " \t\n");
+        skip_to(in, line, pos, ",}", " \t\n");
         c = in.get();
+        pos++;
     }
     return Element::create(m);
 }
 
+//ElementPtr
+//Element::create_from_string(std::stringstream &in)
 ElementPtr
-Element::create_from_string(std::stringstream &in)
+Element::create_from_string(std::istream &in) throw(ParseError)
+{
+    int line = 1, pos = 1;
+    return create_from_string(in, line, pos);
+}
+
+ElementPtr
+Element::create_from_string(std::istream &in, int& line, int& pos) throw(ParseError)
 {
     char c = 0;
     ElementPtr element;
     bool el_read = false;
-    skip_chars(in, " \n\t");
+    skip_chars(in, " \n\t", line, pos);
     while (c != EOF && !el_read) {
         c = in.get();
+        pos++;
+        //std::cout << c << std::endl;
         switch(c) {
             case '1':
             case '2':
@@ -281,7 +330,7 @@ Element::create_from_string(std::stringstream &in)
             case '9':
             case '0':
                 in.putback(c);
-                element = from_stringstream_int_or_double(in);
+                element = from_stringstream_int_or_double(in, line, pos);
                 el_read = true;
                 break;
             case 't':
@@ -289,27 +338,26 @@ Element::create_from_string(std::stringstream &in)
             case 'f':
             case 'F':
                 in.putback(c);
-                element = from_stringstream_bool(in);
+                element = from_stringstream_bool(in, line, pos);
                 el_read = true;
                 break;
             case '"':
                 in.putback('"');
-                element = from_stringstream_string(in);
+                element = from_stringstream_string(in, line, pos);
                 el_read = true;
                 break;
             case '[':
-                element = from_stringstream_list(in);
+                element = from_stringstream_list(in, line, pos);
                 el_read = true;
                 break;
             case '{':
-                element = from_stringstream_map(in);
+                element = from_stringstream_map(in, line, pos);
                 el_read = true;
                 break;
+            case EOF:
+                break;
             default:
-                // TODO this might not be a fatal error
-                // provide feedback mechanism?
-                cout << "error: unexpected '" << c << "'" << endl;
-                return ElementPtr();
+                throw ParseError(std::string("error: unexpected character ") + c, line, pos);
                 break;
         }
     }
@@ -317,7 +365,7 @@ Element::create_from_string(std::stringstream &in)
         return element;
     } else {
         // throw exception?
-        return ElementPtr();
+        throw ParseError("nothing read");
     }
 }
 

+ 15 - 1
src/lib/cc/cpp/data.h

@@ -20,6 +20,17 @@ namespace ISC { namespace Data {
         std::string msg;
     };
 
+    class ParseError : public std::exception {
+    public:
+        ParseError(std::string m = "Parse error in element data", int l = 0, int p = 0) : msg(m), line(l), pos(p) {}
+        ~ParseError() throw() {}
+        const char* what() const throw();
+    private:
+        std::string msg;
+        int line;
+        int pos;
+    };
+
     class DecodeError : public std::exception {
     public:
         DecodeError(std::string m = "Wire-format data is invalid") : msg(m) {}
@@ -145,8 +156,11 @@ namespace ISC { namespace Data {
         // the memory could not be allocated
         // example:
         // ElementPtr my_element = Element::create_from_string("{\"foo\": [ 1, 2, false ] }");
-        static ElementPtr create_from_string(std::stringstream& in);
+        //static ElementPtr create_from_string(std::stringstream& in);
         static ElementPtr create_from_string(const std::string& in);
+        static ElementPtr create_from_string(std::istream& in) throw(ParseError);
+        // make this one private?
+        static ElementPtr create_from_string(std::istream& in, int& line, int &pos) throw(ParseError);
         
         //static ElementPtr create_from_xml(std::stringstream& in);