Browse Source

more parser stuff

David Rose 21 years ago
parent
commit
e4bd5e2c46
31 changed files with 1554 additions and 131 deletions
  1. 4 0
      pandatool/src/xfile/Sources.pp
  2. 6 0
      pandatool/src/xfile/config_xfile.cxx
  3. 7 7
      pandatool/src/xfile/xFile.cxx
  4. 3 3
      pandatool/src/xfile/xFile.h
  5. 24 0
      pandatool/src/xfile/xFileArrayDef.cxx
  6. 3 0
      pandatool/src/xfile/xFileArrayDef.h
  7. 294 0
      pandatool/src/xfile/xFileDataDef.cxx
  8. 35 0
      pandatool/src/xfile/xFileDataDef.h
  9. 26 8
      pandatool/src/xfile/xFileDataObject.I
  10. 80 0
      pandatool/src/xfile/xFileDataObject.cxx
  11. 23 8
      pandatool/src/xfile/xFileDataObject.h
  12. 29 0
      pandatool/src/xfile/xFileDataObjectArray.I
  13. 88 0
      pandatool/src/xfile/xFileDataObjectArray.cxx
  14. 66 0
      pandatool/src/xfile/xFileDataObjectArray.h
  15. 18 0
      pandatool/src/xfile/xFileDataObjectDouble.I
  16. 97 0
      pandatool/src/xfile/xFileDataObjectDouble.cxx
  17. 67 0
      pandatool/src/xfile/xFileDataObjectDouble.h
  18. 18 0
      pandatool/src/xfile/xFileDataObjectInteger.I
  19. 91 0
      pandatool/src/xfile/xFileDataObjectInteger.cxx
  20. 67 0
      pandatool/src/xfile/xFileDataObjectInteger.h
  21. 176 2
      pandatool/src/xfile/xFileDataObjectTemplate.cxx
  22. 18 25
      pandatool/src/xfile/xFileDataObjectTemplate.h
  23. 10 0
      pandatool/src/xfile/xFileNode.I
  24. 36 0
      pandatool/src/xfile/xFileNode.cxx
  25. 20 4
      pandatool/src/xfile/xFileNode.h
  26. 18 0
      pandatool/src/xfile/xFileParseData.I
  27. 50 0
      pandatool/src/xfile/xFileParseData.cxx
  28. 82 0
      pandatool/src/xfile/xFileParseData.h
  29. 87 51
      pandatool/src/xfile/xLexer.lxx
  30. 6 0
      pandatool/src/xfile/xLexerDefs.h
  31. 5 23
      pandatool/src/xfile/xParser.yxx

+ 4 - 0
pandatool/src/xfile/Sources.pp

@@ -20,8 +20,12 @@
      xFileArrayDef.cxx xFileArrayDef.I xFileArrayDef.h \
      xFileDataDef.cxx xFileDataDef.I xFileDataDef.h \
      xFileDataObject.cxx xFileDataObject.I xFileDataObject.h \
+     xFileDataObjectArray.cxx xFileDataObjectArray.I xFileDataObjectArray.h \
+     xFileDataObjectDouble.cxx xFileDataObjectDouble.I xFileDataObjectDouble.h \
+     xFileDataObjectInteger.cxx xFileDataObjectInteger.I xFileDataObjectInteger.h \
      xFileDataObjectTemplate.cxx xFileDataObjectTemplate.I xFileDataObjectTemplate.h \
      xFileNode.cxx xFileNode.I xFileNode.h \
+     xFileParseData.cxx xFileParseData.I xFileParseData.h \
      xFileTemplate.cxx xFileTemplate.I xFileTemplate.h
 
 #end ss_lib_target

+ 6 - 0
pandatool/src/xfile/config_xfile.cxx

@@ -20,6 +20,9 @@
 #include "xFile.h"
 #include "xFileDataDef.h"
 #include "xFileDataObject.h"
+#include "xFileDataObjectArray.h"
+#include "xFileDataObjectDouble.h"
+#include "xFileDataObjectInteger.h"
 #include "xFileDataObjectTemplate.h"
 #include "xFileNode.h"
 #include "xFileTemplate.h"
@@ -58,6 +61,9 @@ init_libxfile() {
   XFile::init_type();
   XFileDataDef::init_type();
   XFileDataObject::init_type();
+  XFileDataObjectArray::init_type();
+  XFileDataObjectDouble::init_type();
+  XFileDataObjectInteger::init_type();
   XFileDataObjectTemplate::init_type();
   XFileNode::init_type();
   XFileTemplate::init_type();

+ 7 - 7
pandatool/src/xfile/xFile.cxx

@@ -20,7 +20,7 @@
 #include "xParserDefs.h"
 #include "xLexerDefs.h"
 #include "xFileTemplate.h"
-#include "xFileDataObject.h"
+#include "xFileDataObjectTemplate.h"
 #include "config_xfile.h"
 #include "standard_templates.h"
 #include "zStream.h"
@@ -250,12 +250,12 @@ find_template(const WindowsGuid &guid) const {
 //  Description: Returns the data object associated with the indicated
 //               name, if any, or NULL if none.
 ////////////////////////////////////////////////////////////////////
-XFileDataObject *XFile::
+XFileDataObjectTemplate *XFile::
 find_data_object(const string &name) const {
   XFileNode *child = find_descendent(name);
   if (child != (XFileNode *)NULL &&
-      child->is_of_type(XFileDataObject::get_class_type())) {
-    return DCAST(XFileDataObject, child);
+      child->is_of_type(XFileDataObjectTemplate::get_class_type())) {
+    return DCAST(XFileDataObjectTemplate, child);
   }
 
   return NULL;
@@ -267,13 +267,13 @@ find_data_object(const string &name) const {
 //  Description: Returns the data object associated with the indicated
 //               GUID, if any, or NULL if none.
 ////////////////////////////////////////////////////////////////////
-XFileDataObject *XFile::
+XFileDataObjectTemplate *XFile::
 find_data_object(const WindowsGuid &guid) const {
   NodesByGuid::const_iterator gi;
   gi = _nodes_by_guid.find(guid);
   if (gi != _nodes_by_guid.end() && 
-      (*gi).second->is_of_type(XFileDataObject::get_class_type())) {
-    return DCAST(XFileDataObject, (*gi).second);
+      (*gi).second->is_of_type(XFileDataObjectTemplate::get_class_type())) {
+    return DCAST(XFileDataObjectTemplate, (*gi).second);
   }
 
   return NULL;

+ 3 - 3
pandatool/src/xfile/xFile.h

@@ -27,7 +27,7 @@
 #include "pointerTo.h"
 
 class XFileTemplate;
-class XFileDataObject;
+class XFileDataObjectTemplate;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : XFile
@@ -51,8 +51,8 @@ public:
   XFileTemplate *find_template(const string &name) const;
   XFileTemplate *find_template(const WindowsGuid &guid) const;
 
-  XFileDataObject *find_data_object(const string &name) const;
-  XFileDataObject *find_data_object(const WindowsGuid &guid) const;
+  XFileDataObjectTemplate *find_data_object(const string &name) const;
+  XFileDataObjectTemplate *find_data_object(const WindowsGuid &guid) const;
 
   virtual void write_text(ostream &out, int indent_level) const;
 

+ 24 - 0
pandatool/src/xfile/xFileArrayDef.cxx

@@ -18,6 +18,30 @@
 
 #include "xFileArrayDef.h"
 #include "xFileDataDef.h"
+#include "xFileDataObject.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileArrayDef::get_size
+//       Access: Public
+//  Description: Returns the size of the array dimension.  If this is
+//               a fixed array, the size is trivial; if it is dynamic,
+//               the size is determined by looking up the dynamic_size
+//               element in the prev_data table (which lists all of
+//               the data values already defined at this scoping
+//               level).
+////////////////////////////////////////////////////////////////////
+int XFileArrayDef::
+get_size(const XFileNode::PrevData &prev_data) const {
+  if (is_fixed_size()) {
+    return _fixed_size;
+  } else {
+    XFileNode::PrevData::const_iterator pi;
+    pi = prev_data.find(_dynamic_size);
+    nassertr(pi != prev_data.end(), 0);
+    nassertr((*pi).second != (XFileDataObject *)NULL, 0);
+    return (*pi).second->i();
+  }
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: XFileArrayDef::output

+ 3 - 0
pandatool/src/xfile/xFileArrayDef.h

@@ -21,6 +21,7 @@
 
 #include "pandatoolbase.h"
 #include "notify.h"
+#include "xFileNode.h"
 
 class XFileDataDef;
 
@@ -38,6 +39,8 @@ public:
   INLINE int get_fixed_size() const;
   INLINE XFileDataDef *get_dynamic_size() const;
 
+  int get_size(const XFileNode::PrevData &prev_data) const;
+
   void output(ostream &out) const;
 
 private:

+ 294 - 0
pandatool/src/xfile/xFileDataDef.cxx

@@ -18,6 +18,12 @@
 
 #include "xFileDataDef.h"
 #include "indent.h"
+#include "xLexerDefs.h"
+#include "xFileParseData.h"
+#include "xFileDataObjectInteger.h"
+#include "xFileDataObjectDouble.h"
+#include "xFileDataObjectTemplate.h"
+#include "xFileDataObjectArray.h"
 
 TypeHandle XFileDataDef::_type_handle;
 
@@ -128,3 +134,291 @@ write_text(ostream &out, int indent_level) const {
 
   out << ";\n";
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataDef::repack_data
+//       Access: Public, Virtual
+//  Description: This is called on the template that defines an
+//               object, once the data for the object has been parsed.
+//               It is responsible for identifying which component of
+//               the template owns each data element, and packing the
+//               data elements appropriately back into the object.
+//
+//               It returns true on success, or false on an error
+//               (e.g. too many semicolons, not enough data elements,
+//               mismatched data type).
+////////////////////////////////////////////////////////////////////
+bool XFileDataDef::
+repack_data(XFileDataObject *object, 
+            const XFileParseDataList &parse_data_list,
+            XFileDataDef::PrevData &prev_data,
+            size_t &index, size_t &sub_index) const {
+  if (index >= parse_data_list._list.size()) {
+    xyyerror("Not enough data elements in structure.");
+    return false;
+  }
+
+  // We'll fill this in with the data value we pack, if any.
+  PT(XFileDataObject) data_value;
+
+  // What kind of data element are we expecting?
+  switch (_type) {
+  case T_word:
+  case T_dword:
+  case T_char:
+  case T_uchar:
+  case T_sword:
+  case T_sdword:
+    // Expected integer data.
+    data_value = unpack_value(parse_data_list, 0,
+                              prev_data, index, sub_index,
+                              XFileParseData::PF_semicolon,
+                              &XFileDataDef::unpack_integer_value);
+    break;
+
+  case T_float:
+  case T_double:
+    data_value = unpack_value(parse_data_list, 0,
+                              prev_data, index, sub_index,
+                              XFileParseData::PF_semicolon,
+                              &XFileDataDef::unpack_double_value);
+    break;
+
+  case T_template:
+    data_value = unpack_value(parse_data_list, 0,
+                              prev_data, index, sub_index,
+                              XFileParseData::PF_semicolon,
+                              &XFileDataDef::unpack_template_value);
+    break;
+
+  default:
+    {
+      const XFileParseData &parse_data = parse_data_list._list[index];
+      parse_data.yyerror("Unexpected data for " + get_name());
+    }
+    return false;
+  }
+
+  if (data_value != (XFileDataObject *)NULL) {
+    if (!object->add_element(data_value)) {
+      // This is really an internal error--this shouldn't happen.
+      const XFileParseData &parse_data = parse_data_list._list[index];
+      parse_data.yyerror("Data does not accept a nested element.");
+    }
+  }
+  prev_data[this] = data_value;
+
+  return XFileNode::repack_data(object, parse_data_list, 
+                                prev_data, index, sub_index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataDef::unpack_integer_value
+//       Access: Private
+//  Description: Unpacks and returns the next sequential integer value
+//               from the parse_data_list.
+////////////////////////////////////////////////////////////////////
+PT(XFileDataObject) XFileDataDef::
+unpack_integer_value(const XFileParseDataList &parse_data_list,
+                     const XFileDataDef::PrevData &prev_data,
+                     size_t &index, size_t &sub_index,
+                     int separator_mask) const {
+  const XFileParseData &parse_data = parse_data_list._list[index];
+
+  PT(XFileDataObject) data_value;
+
+  if ((parse_data._parse_flags & XFileParseData::PF_int) != 0) {
+    nassertr(sub_index < parse_data._int_list.size(), false);
+    int value = parse_data._int_list[sub_index];
+    data_value = new XFileDataObjectInteger(this, value);
+    
+    sub_index++;
+    if (sub_index >= parse_data._int_list.size()) {
+      index++;
+      sub_index = 0;
+    }
+
+    if (separator_mask != 0) {
+      // Now consume a separator character.  These may be defined
+      // implicitly on an integer list.
+      if ((parse_data._parse_flags & separator_mask) == 0) {
+        // The separator we were looking for wasn't what was being
+        // used to delimit the list.  As a special exception, if we
+        // just reached the end of the list and the next token is a
+        // standalone separator that matches what we expect, take that
+        // one.
+        if (sub_index == 0 && index < parse_data_list._list.size() &&
+            parse_data_list._list[index]._parse_flags == separator_mask) {
+          // Bingo!  This is the special case--we incremented past
+          // the end of the list to a standalone separator.
+          index++;
+
+        } else {
+          // Some other case; the separator character we were
+          // expecting isn't to be found.
+          if ((separator_mask & XFileParseData::PF_semicolon) != 0) {
+            parse_data.yyerror("Semicolon expected.");
+          } else {
+            parse_data.yyerror("Comma expected.");
+          }
+        }
+      }
+    }
+
+  } else {
+    parse_data.yyerror("Expected integer data for " + get_name());
+  }
+
+  return data_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataDef::unpack_double_value
+//       Access: Private
+//  Description: Unpacks and returns the next sequential double value
+//               from the parse_data_list.
+////////////////////////////////////////////////////////////////////
+PT(XFileDataObject) XFileDataDef::
+unpack_double_value(const XFileParseDataList &parse_data_list,
+                    const XFileDataDef::PrevData &prev_data,
+                    size_t &index, size_t &sub_index,
+                    int separator_mask) const {
+  const XFileParseData &parse_data = parse_data_list._list[index];
+
+  PT(XFileDataObject) data_value;
+
+  if ((parse_data._parse_flags & XFileParseData::PF_double) != 0) {
+    if (separator_mask != 0 &&
+        (parse_data._parse_flags & separator_mask) == 0) {
+      if ((separator_mask & XFileParseData::PF_semicolon) != 0) {
+        parse_data.yyerror("Semicolon expected.");
+      } else {
+        parse_data.yyerror("Comma expected.");
+      }
+
+    } else {
+      nassertr(sub_index < parse_data._double_list.size(), false);
+      double value = parse_data._double_list[sub_index];
+      data_value = new XFileDataObjectDouble(this, value);
+      
+      sub_index++;
+      if (sub_index >= parse_data._double_list.size()) {
+        index++;
+        sub_index = 0;
+      }
+    }
+
+  } else if ((parse_data._parse_flags & XFileParseData::PF_int) != 0) {
+    if (separator_mask != 0 &&
+        (parse_data._parse_flags & separator_mask) == 0) {
+      if ((separator_mask & XFileParseData::PF_semicolon) != 0) {
+        parse_data.yyerror("Semicolon expected.");
+      } else {
+        parse_data.yyerror("Comma expected.");
+      }
+
+    } else {
+      nassertr(sub_index < parse_data._int_list.size(), false);
+      int value = parse_data._int_list[sub_index];
+      data_value = new XFileDataObjectDouble(this, value);
+      
+      sub_index++;
+      if (sub_index >= parse_data._int_list.size()) {
+        index++;
+        sub_index = 0;
+      }
+    }
+
+  } else {
+    parse_data.yyerror("Expected floating-point data for " + get_name());
+  }
+
+  return data_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataDef::unpack_template_value
+//       Access: Private
+//  Description: Unpacks a nested template object's data.
+////////////////////////////////////////////////////////////////////
+PT(XFileDataObject) XFileDataDef::
+unpack_template_value(const XFileParseDataList &parse_data_list,
+                      const XFileDataDef::PrevData &prev_data,
+                      size_t &index, size_t &sub_index,
+                      int separator_mask) const {
+  PT(XFileDataObjectTemplate) data_value = 
+    new XFileDataObjectTemplate(get_x_file(), get_name(), _template);
+
+  PrevData nested_prev_data(prev_data);
+  if (!_template->repack_data(data_value, parse_data_list, 
+                              nested_prev_data, index, sub_index)) {
+    return NULL;
+  }
+  
+  if (separator_mask != 0) {
+    // Also expect a trailing semicolon or comma.
+    if (index >= parse_data_list._list.size()) {
+      if ((separator_mask & XFileParseData::PF_semicolon) != 0) {
+        xyyerror("Semicolon expected.");
+      } else {
+        xyyerror("Comma expected.");
+      }
+      return NULL;
+    }
+
+    const XFileParseData &new_parse_data = parse_data_list._list[index];
+    if ((new_parse_data._parse_flags & XFileParseData::PF_any_data) != 0 ||
+        (new_parse_data._parse_flags & separator_mask) == 0) {
+      if ((separator_mask & XFileParseData::PF_semicolon) != 0) {
+        new_parse_data.yyerror("Semicolon expected.");
+      } else {
+        new_parse_data.yyerror("Comma expected.");
+      }
+      return false;
+    }
+    index++;
+  }
+
+  return data_value.p();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataDef::unpack_value
+//       Access: Private
+//  Description: Unpacks and returns the next sequential value, of the
+//               type supported by the unpack_method.  If the value
+//               is an array type, unpacks all the elements of the
+//               array.
+////////////////////////////////////////////////////////////////////
+PT(XFileDataObject) XFileDataDef::
+unpack_value(const XFileParseDataList &parse_data_list, int array_index,
+             const XFileDataDef::PrevData &prev_data,
+             size_t &index, size_t &sub_index, int separator_mask,
+             XFileDataDef::UnpackMethod unpack_method) const {
+  PT(XFileDataObject) data_value;
+  
+  if (array_index == (int)_array_def.size()) {
+    data_value = (this->*unpack_method)(parse_data_list, prev_data,
+                                        index, sub_index, separator_mask);
+
+  } else {
+    data_value = new XFileDataObjectArray(this);
+    int array_size = _array_def[array_index].get_size(prev_data);
+
+    for (int i = 0; i < array_size - 1; i++) {
+      PT(XFileDataObject) array_element = 
+        unpack_value(parse_data_list, array_index + 1,
+                     prev_data, index, sub_index,
+                     XFileParseData::PF_comma, unpack_method);
+      data_value->add_element(array_element);
+    }
+    PT(XFileDataObject) array_element = 
+      unpack_value(parse_data_list, array_index + 1,
+                   prev_data, index, sub_index,
+                   separator_mask, unpack_method);
+    data_value->add_element(array_element);
+  }
+
+  return data_value;
+}

+ 35 - 0
pandatool/src/xfile/xFileDataDef.h

@@ -24,6 +24,7 @@
 #include "xFileNode.h"
 #include "xFileArrayDef.h"
 #include "xFileTemplate.h"
+#include "xFileDataObject.h"
 #include "pvector.h"
 #include "pointerTo.h"
 
@@ -67,6 +68,40 @@ public:
 
   virtual void write_text(ostream &out, int indent_level) const;
 
+  virtual bool repack_data(XFileDataObject *object, 
+                           const XFileParseDataList &parse_data_list,
+                           PrevData &prev_data,
+                           size_t &index, size_t &sub_index) const;
+
+private:
+  typedef PT(XFileDataObject) 
+    (XFileDataDef::*UnpackMethod)(const XFileParseDataList &parse_data_list, 
+                                  const PrevData &prev_data,
+                                  size_t &index, size_t &sub_index,
+                                  int separator_mask) const;
+
+  PT(XFileDataObject) 
+    unpack_integer_value(const XFileParseDataList &parse_data_list,
+                         const PrevData &prev_data,
+                         size_t &index, size_t &sub_index,
+                         int separator_mask) const;
+  PT(XFileDataObject) 
+    unpack_double_value(const XFileParseDataList &parse_data_list,
+                        const PrevData &prev_data,
+                        size_t &index, size_t &sub_index,
+                        int separator_mask) const;
+  PT(XFileDataObject) 
+    unpack_template_value(const XFileParseDataList &parse_data_list,
+                          const PrevData &prev_data,
+                          size_t &index, size_t &sub_index,
+                          int separator_mask) const;
+
+  PT(XFileDataObject) 
+    unpack_value(const XFileParseDataList &parse_data_list, int array_index,
+                 const PrevData &prev_data,
+                 size_t &index, size_t &sub_index, int separator_mask,
+                 UnpackMethod unpack_method) const;
+    
 private:
   Type _type;
   PT(XFileTemplate) _template;

+ 26 - 8
pandatool/src/xfile/xFileDataObject.I

@@ -23,8 +23,8 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE XFileDataObject::
-XFileDataObject(XFile *x_file, const string &name) : 
-  XFileNode(x_file, name)
+XFileDataObject(const XFileDataDef *data_def) :
+  _data_def(data_def)
 {
 }
 
@@ -34,7 +34,7 @@ XFileDataObject(XFile *x_file, const string &name) :
 //  Description: Returns the data object that this object is
 //               represented by, if any, or NULL if there is none.
 ////////////////////////////////////////////////////////////////////
-INLINE XFileDataDef *XFileDataObject::
+INLINE const XFileDataDef *XFileDataObject::
 get_data_def() const {
   return _data_def;
 }
@@ -115,6 +115,17 @@ operator string () const {
   return as_string_value();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::size
+//       Access: Public
+//  Description: Returns the number of nested data objects within this
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE int XFileDataObject::
+size() const {
+  return get_num_elements();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: XFileDataObject::operator [] (int)
 //       Access: Public
@@ -124,8 +135,9 @@ operator string () const {
 ////////////////////////////////////////////////////////////////////
 INLINE const XFileDataObject &XFileDataObject::
 operator [] (int n) const {
-  nassertr(n >= 0 && n < get_num_children(), *this);
-  return *DCAST(XFileDataObject, get_child(n));
+  const XFileDataObject *element = get_element(n);
+  nassertr(element != (XFileDataObject *)NULL, *this);
+  return *element;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -137,7 +149,13 @@ operator [] (int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE const XFileDataObject &XFileDataObject::
 operator [] (const string &name) const {
-  XFileNode *child = find_child(name);
-  nassertr(child != (XFileNode *)NULL, *this);
-  return *DCAST(XFileDataObject, child);
+  const XFileDataObject *element = get_element(name);
+  nassertr(element != (XFileDataObject *)NULL, *this);
+  return *element;
+}
+
+INLINE ostream &
+operator << (ostream &out, const XFileDataObject &data_object) {
+  data_object.output_data(out);
+  return out;
 }

+ 80 - 0
pandatool/src/xfile/xFileDataObject.cxx

@@ -17,9 +17,55 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "xFileDataObject.h"
+#include "indent.h"
 
 TypeHandle XFileDataObject::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+XFileDataObject::
+~XFileDataObject() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::add_element
+//       Access: Public, Virtual
+//  Description: Adds the indicated element as a nested data element,
+//               if this data object type supports it.  Returns true
+//               if added successfully, false if the data object type
+//               does not support nested data elements.
+////////////////////////////////////////////////////////////////////
+bool XFileDataObject::
+add_element(XFileDataObject *element) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::output_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObject::
+output_data(ostream &out) const {
+  out << "(" << get_type() << "::output_data() not implemented.)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::write_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObject::
+write_data(ostream &out, int indent_level, const char *) const {
+  indent(out, indent_level)
+    << "(" << get_type() << "::write_data() not implemented.)\n";
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: XFileDataObject::as_integer_value
 //       Access: Protected, Virtual
@@ -52,3 +98,37 @@ string XFileDataObject::
 as_string_value() const {
   return string();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::get_num_elements
+//       Access: Protected, Virtual
+//  Description: Returns the number of nested data elements within the
+//               object.  This may be, e.g. the size of the array, if
+//               it is an array.
+////////////////////////////////////////////////////////////////////
+int XFileDataObject::
+get_num_elements() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::get_element
+//       Access: Protected, Virtual
+//  Description: Returns the nth nested data element within the
+//               object.
+////////////////////////////////////////////////////////////////////
+const XFileDataObject *XFileDataObject::
+get_element(int n) const {
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObject::get_element
+//       Access: Protected, Virtual
+//  Description: Returns the nested data element within the
+//               object that has the indicated name.
+////////////////////////////////////////////////////////////////////
+const XFileDataObject *XFileDataObject::
+get_element(const string &name) const {
+  return NULL;
+}

+ 23 - 8
pandatool/src/xfile/xFileDataObject.h

@@ -20,22 +20,24 @@
 #define XFILEDATAOBJECT_H
 
 #include "pandatoolbase.h"
-#include "xFileNode.h"
-#include "xFileDataDef.h"
+#include "referenceCount.h"
 #include "pointerTo.h"
 #include "dcast.h"
 
+class XFileDataDef;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : XFileDataObject
 // Description : The abstract base class for a number of different
 //               types of data elements that may be stored in the X
 //               file.
 ////////////////////////////////////////////////////////////////////
-class XFileDataObject : public XFileNode {
+class XFileDataObject : virtual public ReferenceCount {
 public:
-  INLINE XFileDataObject(XFile *x_file, const string &name);
+  INLINE XFileDataObject(const XFileDataDef *data_def = NULL);
+  virtual ~XFileDataObject();
 
-  INLINE XFileDataDef *get_data_def() const;
+  INLINE const XFileDataDef *get_data_def() const;
 
   INLINE int i() const;
   INLINE double d() const;
@@ -44,24 +46,35 @@ public:
   INLINE operator double () const;
   INLINE operator string () const;
 
+  INLINE int size() const;
   INLINE const XFileDataObject &operator [] (int n) const;
   INLINE const XFileDataObject &operator [] (const string &name) const;
 
+  virtual bool add_element(XFileDataObject *element);
+
+  virtual void output_data(ostream &out) const;
+  virtual void write_data(ostream &out, int indent_level,
+                          const char *separator) const;
+
 protected:
   virtual int as_integer_value() const;
   virtual double as_double_value() const;
   virtual string as_string_value() const;
 
-  PT(XFileDataDef) _data_def;
+  virtual int get_num_elements() const;
+  virtual const XFileDataObject *get_element(int n) const;
+  virtual const XFileDataObject *get_element(const string &name) const;
+
+  const XFileDataDef *_data_def;
 
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    XFileNode::init_type();
+    ReferenceCount::init_type();
     register_type(_type_handle, "XFileDataObject",
-                  XFileNode::get_class_type());
+                  ReferenceCount::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();
@@ -72,6 +85,8 @@ private:
   static TypeHandle _type_handle;
 };
 
+INLINE ostream &operator << (ostream &out, const XFileDataObject &data_object);
+
 #include "xFileDataObject.I"
 
 #endif

+ 29 - 0
pandatool/src/xfile/xFileDataObjectArray.I

@@ -0,0 +1,29 @@
+// Filename: xFileDataObjectArray.I
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectArray::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+XFileDataObjectArray::
+XFileDataObjectArray(const XFileDataDef *data_def) :
+  XFileDataObject(data_def)
+{
+}

+ 88 - 0
pandatool/src/xfile/xFileDataObjectArray.cxx

@@ -0,0 +1,88 @@
+// Filename: xFileDataObjectArray.cxx
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "xFileDataObjectArray.h"
+#include "string_utils.h"
+#include "indent.h"
+
+TypeHandle XFileDataObjectArray::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectArray::write_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectArray::
+write_data(ostream &out, int indent_level, const char *separator) const {
+  if (!_nested_elements.empty()) {
+    if (_nested_elements.front()->size() != 0) {
+      // If we have a complex nested structure, output one per line.
+      for (size_t i = 0; i < _nested_elements.size() - 1; i++) {
+        _nested_elements[i]->write_data(out, indent_level, ",");
+      }
+      _nested_elements.back()->write_data(out, indent_level, separator);
+
+    } else {
+      // Otherwise, output them all on the same line.
+      indent(out, indent_level);
+      for (size_t i = 0; i < _nested_elements.size() - 1; i++) {
+        out << *_nested_elements[i] << ", ";
+      }
+      out << *_nested_elements.back() << separator << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectArray::add_element
+//       Access: Public, Virtual
+//  Description: Adds the indicated element as a nested data element,
+//               if this data object type supports it.  Returns true
+//               if added successfully, false if the data object type
+//               does not support nested data elements.
+////////////////////////////////////////////////////////////////////
+bool XFileDataObjectArray::
+add_element(XFileDataObject *element) {
+  _nested_elements.push_back(element);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectArray::get_num_elements
+//       Access: Protected, Virtual
+//  Description: Returns the number of nested data elements within the
+//               object.  This may be, e.g. the size of the array, if
+//               it is an array.
+////////////////////////////////////////////////////////////////////
+int XFileDataObjectArray::
+get_num_elements() const {
+  return _nested_elements.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectArray::get_element
+//       Access: Protected, Virtual
+//  Description: Returns the nth nested data element within the
+//               object.
+////////////////////////////////////////////////////////////////////
+const XFileDataObject *XFileDataObjectArray::
+get_element(int n) const {
+  nassertr(n >= 0 && n < (int)_nested_elements.size(), NULL);
+  return _nested_elements[n];
+}

+ 66 - 0
pandatool/src/xfile/xFileDataObjectArray.h

@@ -0,0 +1,66 @@
+// Filename: xFileDataObjectArray.h
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef XFILEDATAOBJECTARRAY_H
+#define XFILEDATAOBJECTARRAY_H
+
+#include "pandatoolbase.h"
+#include "xFileDataObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : XFileDataObjectArray
+// Description : An array of nested data elements.
+////////////////////////////////////////////////////////////////////
+class XFileDataObjectArray : public XFileDataObject {
+public:
+  INLINE XFileDataObjectArray(const XFileDataDef *data_def);
+
+  virtual void write_data(ostream &out, int indent_level,
+                          const char *separator) const;
+
+  virtual bool add_element(XFileDataObject *element);
+
+protected:
+  virtual int get_num_elements() const;
+  virtual const XFileDataObject *get_element(int n) const;
+
+private:
+  typedef pvector< PT(XFileDataObject) > NestedElements;
+  NestedElements _nested_elements;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    XFileDataObject::init_type();
+    register_type(_type_handle, "XFileDataObjectArray",
+                  XFileDataObject::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "xFileDataObjectArray.I"
+
+#endif

+ 18 - 0
pandatool/src/xfile/xFileDataObjectDouble.I

@@ -0,0 +1,18 @@
+// Filename: xFileDataObjectDouble.I
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 97 - 0
pandatool/src/xfile/xFileDataObjectDouble.cxx

@@ -0,0 +1,97 @@
+// Filename: xFileDataObjectDouble.cxx
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "xFileDataObjectDouble.h"
+#include "string_utils.h"
+#include "indent.h"
+
+TypeHandle XFileDataObjectDouble::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+XFileDataObjectDouble::
+XFileDataObjectDouble(const XFileDataDef *data_def, double value) :
+  XFileDataObject(data_def),
+  _value(value)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::output_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectDouble::
+output_data(ostream &out) const {
+  out << as_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::write_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectDouble::
+write_data(ostream &out, int indent_level, const char *separator) const {
+  indent(out, indent_level)
+    << as_string_value() << separator << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::as_integer_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as an integer, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+int XFileDataObjectDouble::
+as_integer_value() const {
+  return (int)_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::as_double_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as a double, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+double XFileDataObjectDouble::
+as_double_value() const {
+  return _value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectDouble::as_string_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as a string, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+string XFileDataObjectDouble::
+as_string_value() const {
+  // It's important to format with a decimal point, even if the value
+  // is integral, since the DirectX .x reader differentiates betweens
+  // doubles and integers on parsing.
+  char buffer[128];
+  sprintf(buffer, "%f", _value);
+  
+  return buffer;
+}

+ 67 - 0
pandatool/src/xfile/xFileDataObjectDouble.h

@@ -0,0 +1,67 @@
+// Filename: xFileDataObjectDouble.h
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef XFILEDATAOBJECTDOUBLE_H
+#define XFILEDATAOBJECTDOUBLE_H
+
+#include "pandatoolbase.h"
+#include "xFileDataObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : XFileDataObjectDouble
+// Description : An double-valued data element.  This matches one
+//               double data member of a template, or a single
+//               element of an double array.
+////////////////////////////////////////////////////////////////////
+class XFileDataObjectDouble : public XFileDataObject {
+public:
+  XFileDataObjectDouble(const XFileDataDef *data_def, double value);
+
+  virtual void output_data(ostream &out) const;
+  virtual void write_data(ostream &out, int indent_level,
+                          const char *separator) const;
+
+protected:
+  virtual int as_integer_value() const;
+  virtual double as_double_value() const;
+  virtual string as_string_value() const;
+
+private:
+  double _value;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    XFileDataObject::init_type();
+    register_type(_type_handle, "XFileDataObjectDouble",
+                  XFileDataObject::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "xFileDataObjectDouble.I"
+
+#endif

+ 18 - 0
pandatool/src/xfile/xFileDataObjectInteger.I

@@ -0,0 +1,18 @@
+// Filename: xFileDataObjectInteger.I
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 91 - 0
pandatool/src/xfile/xFileDataObjectInteger.cxx

@@ -0,0 +1,91 @@
+// Filename: xFileDataObjectInteger.cxx
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "xFileDataObjectInteger.h"
+#include "string_utils.h"
+#include "indent.h"
+
+TypeHandle XFileDataObjectInteger::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+XFileDataObjectInteger::
+XFileDataObjectInteger(const XFileDataDef *data_def, int value) :
+  XFileDataObject(data_def),
+  _value(value)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::output_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectInteger::
+output_data(ostream &out) const {
+  out << _value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::write_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectInteger::
+write_data(ostream &out, int indent_level, const char *separator) const {
+  indent(out, indent_level)
+    << _value << separator << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::as_integer_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as an integer, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+int XFileDataObjectInteger::
+as_integer_value() const {
+  return _value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::as_double_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as a double, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+double XFileDataObjectInteger::
+as_double_value() const {
+  return _value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectInteger::as_string_value
+//       Access: Protected, Virtual
+//  Description: Returns the object's representation as a string, if
+//               it has one.
+////////////////////////////////////////////////////////////////////
+string XFileDataObjectInteger::
+as_string_value() const {
+  return format_string(_value);
+}

+ 67 - 0
pandatool/src/xfile/xFileDataObjectInteger.h

@@ -0,0 +1,67 @@
+// Filename: xFileDataObjectInteger.h
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef XFILEDATAOBJECTINTEGER_H
+#define XFILEDATAOBJECTINTEGER_H
+
+#include "pandatoolbase.h"
+#include "xFileDataObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : XFileDataObjectInteger
+// Description : An integer-valued data element.  This matches one
+//               integer data member of a template, or a single
+//               element of an integer array.
+////////////////////////////////////////////////////////////////////
+class XFileDataObjectInteger : public XFileDataObject {
+public:
+  XFileDataObjectInteger(const XFileDataDef *data_def, int value);
+
+  virtual void output_data(ostream &out) const;
+  virtual void write_data(ostream &out, int indent_level,
+                          const char *separator) const;
+
+protected:
+  virtual int as_integer_value() const;
+  virtual double as_double_value() const;
+  virtual string as_string_value() const;
+
+private:
+  int _value;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    XFileDataObject::init_type();
+    register_type(_type_handle, "XFileDataObjectInteger",
+                  XFileDataObject::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "xFileDataObjectInteger.I"
+
+#endif

+ 176 - 2
pandatool/src/xfile/xFileDataObjectTemplate.cxx

@@ -18,6 +18,8 @@
 
 #include "xFileDataObjectTemplate.h"
 #include "indent.h"
+#include "xFileParseData.h"
+#include "xLexerDefs.h"
 
 TypeHandle XFileDataObjectTemplate::_type_handle;
 
@@ -29,7 +31,7 @@ TypeHandle XFileDataObjectTemplate::_type_handle;
 XFileDataObjectTemplate::
 XFileDataObjectTemplate(XFile *x_file, const string &name,
                         XFileTemplate *xtemplate) :
-  XFileDataObject(x_file, name),
+  XFileNode(x_file, name),
   _template(xtemplate)
 {
 }
@@ -48,11 +50,46 @@ write_text(ostream &out, int indent_level) const {
     out << " " << get_name();
   }
   out << " {\n";
-  XFileDataObject::write_text(out, indent_level + 2);
+
+  int num_elements = get_num_elements();
+  for (int i = 0; i < num_elements; i++) {
+    get_element(i)->write_data(out, indent_level + 2, ";");
+  }
+
+  XFileNode::write_text(out, indent_level + 2);
   indent(out, indent_level)
     << "}\n";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectTemplate::write_data
+//       Access: Public, Virtual
+//  Description: Writes a suitable representation of this node to an
+//               .x file in text mode.
+////////////////////////////////////////////////////////////////////
+void XFileDataObjectTemplate::
+write_data(ostream &out, int indent_level, const char *separator) const {
+  if (!_nested_elements.empty()) {
+    if (_nested_elements.front()->size() != 0) {
+      // If we have a complex nested structure, output one per line.
+      for (size_t i = 0; i < _nested_elements.size() - 1; i++) {
+        _nested_elements[i]->write_data(out, indent_level, ";");
+      }
+      string combined_separator = string(";") + string(separator);
+      _nested_elements.back()->write_data(out, indent_level, 
+                                          combined_separator.c_str());
+
+    } else {
+      // Otherwise, output them all on the same line.
+      indent(out, indent_level);
+      for (size_t i = 0; i < _nested_elements.size() - 1; i++) {
+        out << *_nested_elements[i] << "; ";
+      }
+      out << *_nested_elements.back() << ";" << separator << "\n";
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: XFileDataObjectTemplate::add_parse_object
 //       Access: Public
@@ -62,6 +99,14 @@ write_text(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 void XFileDataObjectTemplate::
 add_parse_object(XFileDataObjectTemplate *object, bool reference) {
+  XFileParseData pdata;
+  pdata._object = object;
+  pdata._parse_flags = XFileParseData::PF_object;
+  if (reference) {
+    pdata._parse_flags |= XFileParseData::PF_reference;
+  }
+
+  _parse_data_list._list.push_back(pdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -73,6 +118,20 @@ add_parse_object(XFileDataObjectTemplate *object, bool reference) {
 ////////////////////////////////////////////////////////////////////
 void XFileDataObjectTemplate::
 add_parse_double(PTA_double double_list, char separator) {
+  XFileParseData pdata;
+  pdata._double_list = double_list;
+  pdata._parse_flags = XFileParseData::PF_double;
+  switch (separator) {
+  case ',':
+    pdata._parse_flags |= XFileParseData::PF_comma;
+    break;
+
+  case ';':
+    pdata._parse_flags |= XFileParseData::PF_semicolon;
+    break;
+  }
+
+  _parse_data_list._list.push_back(pdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -84,6 +143,20 @@ add_parse_double(PTA_double double_list, char separator) {
 ////////////////////////////////////////////////////////////////////
 void XFileDataObjectTemplate::
 add_parse_int(PTA_int int_list, char separator) {
+  XFileParseData pdata;
+  pdata._int_list = int_list;
+  pdata._parse_flags = XFileParseData::PF_int;
+  switch (separator) {
+  case ',':
+    pdata._parse_flags |= XFileParseData::PF_comma;
+    break;
+
+  case ';':
+    pdata._parse_flags |= XFileParseData::PF_semicolon;
+    break;
+  }
+
+  _parse_data_list._list.push_back(pdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -95,6 +168,20 @@ add_parse_int(PTA_int int_list, char separator) {
 ////////////////////////////////////////////////////////////////////
 void XFileDataObjectTemplate::
 add_parse_string(const string &str, char separator) {
+  XFileParseData pdata;
+  pdata._string = str;
+  pdata._parse_flags = XFileParseData::PF_string;
+  switch (separator) {
+  case ',':
+    pdata._parse_flags |= XFileParseData::PF_comma;
+    break;
+
+  case ';':
+    pdata._parse_flags |= XFileParseData::PF_semicolon;
+    break;
+  }
+
+  _parse_data_list._list.push_back(pdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -106,6 +193,19 @@ add_parse_string(const string &str, char separator) {
 ////////////////////////////////////////////////////////////////////
 void XFileDataObjectTemplate::
 add_parse_separator(char separator) {
+  XFileParseData pdata;
+  pdata._parse_flags = 0;
+  switch (separator) {
+  case ',':
+    pdata._parse_flags |= XFileParseData::PF_comma;
+    break;
+
+  case ';':
+    pdata._parse_flags |= XFileParseData::PF_semicolon;
+    break;
+  }
+
+  _parse_data_list._list.push_back(pdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -119,5 +219,79 @@ add_parse_separator(char separator) {
 ////////////////////////////////////////////////////////////////////
 bool XFileDataObjectTemplate::
 finalize_parse_data() {
+  // Recursively walk through our template definition, while
+  // simultaneously walking through the list of parse data elements we
+  // encountered, and re-pack them as actual nested elements.
+  PrevData prev_data;
+  size_t index = 0;
+  size_t sub_index = 0;
+
+  if (!_template->repack_data(this, _parse_data_list, 
+                              prev_data, index, sub_index)) {
+    return false;
+  }
+
+  // Quietly allow an extra semicolon at the end of the structure.
+  // (Why is this sometimes here?)
+  if (index < _parse_data_list._list.size() &&
+      _parse_data_list._list[index]._parse_flags == XFileParseData::PF_semicolon) {
+    index++;
+  }
+
+  if (index != _parse_data_list._list.size()) {
+    cerr << "flags = " << hex << _parse_data_list._list[index]._parse_flags << dec << "\n";
+    xyyerror("Too many data elements in structure.");
+    return false;
+  }
+  
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectTemplate::add_element
+//       Access: Public, Virtual
+//  Description: Adds the indicated element as a nested data element,
+//               if this data object type supports it.  Returns true
+//               if added successfully, false if the data object type
+//               does not support nested data elements.
+////////////////////////////////////////////////////////////////////
+bool XFileDataObjectTemplate::
+add_element(XFileDataObject *element) {
+  _nested_elements.push_back(element);
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectTemplate::get_num_elements
+//       Access: Protected, Virtual
+//  Description: Returns the number of nested data elements within the
+//               object.  This may be, e.g. the size of the array, if
+//               it is an array.
+////////////////////////////////////////////////////////////////////
+int XFileDataObjectTemplate::
+get_num_elements() const {
+  return _nested_elements.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectTemplate::get_element
+//       Access: Protected, Virtual
+//  Description: Returns the nth nested data element within the
+//               object.
+////////////////////////////////////////////////////////////////////
+const XFileDataObject *XFileDataObjectTemplate::
+get_element(int n) const {
+  nassertr(n >= 0 && n < (int)_nested_elements.size(), NULL);
+  return _nested_elements[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileDataObjectTemplate::get_element
+//       Access: Protected, Virtual
+//  Description: Returns the nested data element within the
+//               object that has the indicated name.
+////////////////////////////////////////////////////////////////////
+const XFileDataObject *XFileDataObjectTemplate::
+get_element(const string &name) const {
+  return NULL;
+}

+ 18 - 25
pandatool/src/xfile/xFileDataObjectTemplate.h

@@ -20,8 +20,10 @@
 #define XFILEDATAOBJECTTEMPLATE_H
 
 #include "pandatoolbase.h"
+#include "xFileNode.h"
 #include "xFileDataObject.h"
 #include "xFileTemplate.h"
+#include "xFileParseData.h"
 #include "pointerTo.h"
 #include "pta_int.h"
 #include "pta_double.h"
@@ -34,7 +36,7 @@
 //               obtained by walking through the children of this
 //               object.
 ////////////////////////////////////////////////////////////////////
-class XFileDataObjectTemplate : public XFileDataObject {
+class XFileDataObjectTemplate : public XFileNode, public XFileDataObject {
 public:
   XFileDataObjectTemplate(XFile *x_file, const string &name,
                           XFileTemplate *xtemplate);
@@ -42,6 +44,8 @@ public:
   INLINE XFileTemplate *get_template() const;
 
   virtual void write_text(ostream &out, int indent_level) const;
+  virtual void write_data(ostream &out, int indent_level,
+                          const char *separator) const;
 
 public:
   void add_parse_object(XFileDataObjectTemplate *object, bool reference);
@@ -51,41 +55,30 @@ public:
   void add_parse_separator(char separator);
   bool finalize_parse_data();
 
+  virtual bool add_element(XFileDataObject *element);
+
+protected:
+  virtual int get_num_elements() const;
+  virtual const XFileDataObject *get_element(int n) const;
+  virtual const XFileDataObject *get_element(const string &name) const;
+
 private:
   PT(XFileTemplate) _template;
 
-  // This class is used to fill up the data as the values are parsed.
-  // It only has a temporary lifespan; it will be converted into
-  // actual data by finalize_parse_data().
-  enum ParseFlags {
-    PF_object     = 0x001,
-    PF_reference  = 0x002,
-    PF_double     = 0x004,
-    PF_int        = 0x008,
-    PF_string     = 0x010,
-    PF_comma      = 0x020,
-    PF_semicolon  = 0x040,
-  };
-
-  class ParseData {
-  public:
-    PT(XFileDataObjectTemplate) _object;
-    PTA_double _double_list;
-    PTA_int _int_list;
-    string _string;
-    int _parse_flags;
-  };
-
-  typedef pvector<ParseData> ParseDataList;
-  ParseDataList _parse_data_list;
+  XFileParseDataList _parse_data_list;
   
+  typedef pvector< PT(XFileDataObject) > NestedElements;
+  NestedElements _nested_elements;
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
+    XFileNode::init_type();
     XFileDataObject::init_type();
     register_type(_type_handle, "XFileDataObjectTemplate",
+                  XFileNode::get_class_type(),
                   XFileDataObject::get_class_type());
   }
   virtual TypeHandle get_type() const {

+ 10 - 0
pandatool/src/xfile/xFileNode.I

@@ -17,6 +17,16 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: XFileNode::get_num_children
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE XFile *XFileNode::
+get_x_file() const {
+  return _x_file;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: XFileNode::get_num_children
 //       Access: Public

+ 36 - 0
pandatool/src/xfile/xFileNode.cxx

@@ -19,6 +19,8 @@
 #include "xFileNode.h"
 #include "windowsGuid.h"
 #include "xFile.h"
+#include "xLexerDefs.h"
+#include "xFileParseData.h"
 
 TypeHandle XFileNode::_type_handle;
 
@@ -149,3 +151,37 @@ write_text(ostream &out, int indent_level) const {
     (*ci)->write_text(out, indent_level);
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileNode::repack_data
+//       Access: Public, Virtual
+//  Description: This is called on the template that defines an
+//               object, once the data for the object has been parsed.
+//               It is responsible for identifying which component of
+//               the template owns each data element, and packing the
+//               data elements appropriately back into the object.
+//
+//               It returns true on success, or false on an error
+//               (e.g. too many semicolons, not enough data elements,
+//               mismatched data type).
+////////////////////////////////////////////////////////////////////
+bool XFileNode::
+repack_data(XFileDataObject *object, 
+            const XFileParseDataList &parse_data_list,
+            XFileNode::PrevData &prev_data,
+            size_t &index, size_t &sub_index) const {
+  // This method should be specialized for data types that actually
+  // consume a data element.  Here in the base class, it just walks
+  // through its children, asking each one to pull off the appropriate
+  // number of data elements.
+
+  Children::const_iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    if (!(*ci)->repack_data(object, parse_data_list, 
+                            prev_data, index, sub_index)) {
+      return false;
+    }
+  }
+
+  return true;
+}

+ 20 - 4
pandatool/src/xfile/xFileNode.h

@@ -20,7 +20,8 @@
 #define XFILENODE_H
 
 #include "pandatoolbase.h"
-#include "typedReferenceCount.h"
+#include "typedObject.h"
+#include "referenceCount.h"
 #include "pointerTo.h"
 #include "namable.h"
 #include "notify.h"
@@ -29,17 +30,23 @@
 
 class XFile;
 class WindowsGuid;
+class XFileParseDataList;
+class XFileDataDef;
+class XFileDataObject;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : XFileNode
 // Description : A single node of an X file.  This may be either a
 //               template or a data node.
 ////////////////////////////////////////////////////////////////////
-class XFileNode : public TypedReferenceCount, public Namable {
+class XFileNode : public TypedObject, public Namable,
+                  virtual public ReferenceCount {
 public:
   XFileNode(XFile *x_file, const string &name);
   virtual ~XFileNode();
 
+  INLINE XFile *get_x_file() const;
+
   INLINE int get_num_children() const;
   INLINE XFileNode *get_child(int n) const;
   XFileNode *find_child(const string &name) const;
@@ -53,6 +60,13 @@ public:
 
   virtual void write_text(ostream &out, int indent_level) const;
 
+  typedef pmap<const XFileDataDef *, XFileDataObject *> PrevData;
+
+  virtual bool repack_data(XFileDataObject *object, 
+                           const XFileParseDataList &parse_data_list,
+                           PrevData &prev_data,
+                           size_t &index, size_t &sub_index) const;
+
 protected:
   XFile *_x_file;
   
@@ -67,9 +81,11 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    TypedReferenceCount::init_type();
+    TypedObject::init_type();
+    ReferenceCount::init_type();
     register_type(_type_handle, "XFileNode",
-                  TypedReferenceCount::get_class_type());
+                  TypedObject::get_class_type(),
+                  ReferenceCount::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 18 - 0
pandatool/src/xfile/xFileParseData.I

@@ -0,0 +1,18 @@
+// Filename: xFileParseData.I
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 50 - 0
pandatool/src/xfile/xFileParseData.cxx

@@ -0,0 +1,50 @@
+// Filename: xFileParseData.cxx
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "xFileParseData.h"
+#include "xLexerDefs.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileParseData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+XFileParseData::
+XFileParseData() :
+  _parse_flags(0)
+{
+  // Save the line number, column number, and line text in case we
+  // detect an error later and want to report a meaningful message to
+  // the user.
+  _line_number = x_line_number;
+  _col_number = x_col_number;
+  _current_line = x_current_line;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: XFileParseData::yyerror
+//       Access: Public
+//  Description: Reports a parsing error message to the user, showing
+//               the line and column from which this object was
+//               originally parsed.
+////////////////////////////////////////////////////////////////////
+void XFileParseData::
+yyerror(const string &message) const {
+  xyyerror(message, _line_number, _col_number, _current_line);
+}

+ 82 - 0
pandatool/src/xfile/xFileParseData.h

@@ -0,0 +1,82 @@
+// Filename: xFileParseData.h
+// Created by:  drose (07Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef XFILEPARSEDATA_H
+#define XFILEPARSEDATA_H
+
+#include "pandatoolbase.h"
+#include "xFileDataObject.h"
+#include "pointerTo.h"
+#include "pta_int.h"
+#include "pta_double.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : XFileParseData
+// Description : This class is used to fill up the data into an
+//               XFileDataObjectTemplate object as the data values are
+//               parsed out of the X file.  It only has a temporary
+//               lifespan; it will be converted into actual data by
+//               XFileDataObjectTemplate::finalize_parse_data().
+////////////////////////////////////////////////////////////////////
+class XFileParseData {
+public:
+  XFileParseData();
+
+  void yyerror(const string &message) const;
+
+  enum ParseFlags {
+    PF_object     = 0x001,
+    PF_reference  = 0x002,
+    PF_double     = 0x004,
+    PF_int        = 0x008,
+    PF_string     = 0x010,
+    PF_any_data   = 0x01f,
+    PF_comma      = 0x020,
+    PF_semicolon  = 0x040,
+  };
+
+  PT(XFileDataObject) _object;
+  PTA_double _double_list;
+  PTA_int _int_list;
+  string _string;
+  int _parse_flags;
+
+  int _line_number;
+  int _col_number;
+  string _current_line;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : XFileParseDataList
+// Description : A container for a pvector of the above objects.  We
+//               need this wrapper class to avoid circular #includes;
+//               this allows XFileNode to define a forward reference
+//               to this class (without having to include this file or
+//               know that it contains a template class).
+////////////////////////////////////////////////////////////////////
+class XFileParseDataList {
+public:
+  typedef pvector<XFileParseData> List;
+  List _list;
+};
+
+#include "xFileParseData.I"
+
+#endif
+

+ 87 - 51
pandatool/src/xfile/xLexer.lxx

@@ -11,7 +11,7 @@
 #include "xParser.h"
 #include "indent.h"
 #include "string_utils.h"
-#include "notify.h"
+#include "config_xfile.h"
 
 static int yyinput(void);        // declared by flex.
 extern "C" int xyywrap();
@@ -22,13 +22,13 @@ extern "C" int xyywrap();
 
 // We'll increment line_number and col_number as we parse the file, so
 // that we can report the position of an error.
-static int line_number = 0;
-static int col_number = 0;
+int x_line_number = 0;
+int x_col_number = 0;
 
 // current_line holds as much of the current line as will fit.  Its
 // only purpose is for printing it out to report an error to the user.
 static const int max_error_width = 1024;
-static char current_line[max_error_width + 1];
+char x_current_line[max_error_width + 1];
 
 static int error_count = 0;
 static int warning_count = 0;
@@ -49,8 +49,8 @@ void
 x_init_lexer(istream &in, const string &filename) {
   inp = &in;
   x_filename = filename;
-  line_number = 0;
-  col_number = 0;
+  x_line_number = 0;
+  x_col_number = 0;
   error_count = 0;
   warning_count = 0;
 }
@@ -77,14 +77,20 @@ xyywrap(void) {
 
 void
 xyyerror(const string &msg) {
-  cerr << "\nError";
+  xyyerror(msg, x_line_number, x_col_number, x_current_line);
+}
+
+void
+xyyerror(const string &msg, int line_number, int col_number, 
+         const string &current_line) {
+  xfile_cat.error(false) << "\nError";
   if (!x_filename.empty()) {
-    cerr << " in " << x_filename;
+    xfile_cat.error(false) << " in " << x_filename;
   }
-  cerr 
+  xfile_cat.error(false) 
     << " at line " << line_number << ", column " << col_number << ":\n"
     << current_line << "\n";
-  indent(cerr, col_number-1) 
+  indent(xfile_cat.error(false), col_number-1) 
     << "^\n" << msg << "\n\n";
   
   error_count++;
@@ -92,14 +98,14 @@ xyyerror(const string &msg) {
 
 void
 xyywarning(const string &msg) {
-  cerr << "\nWarning";
+  xfile_cat.warning(false) << "\nWarning";
   if (!x_filename.empty()) {
-    cerr << " in " << x_filename;
+    xfile_cat.warning(false) << " in " << x_filename;
   }
-  cerr 
-    << " at line " << line_number << ", column " << col_number << ":\n"
-    << current_line << "\n";
-  indent(cerr, col_number-1) 
+  xfile_cat.warning(false) 
+    << " at line " << x_line_number << ", column " << x_col_number << ":\n"
+    << x_current_line << "\n";
+  indent(xfile_cat.warning(false), x_col_number-1) 
     << "^\n" << msg << "\n\n";
 
   warning_count++;
@@ -118,18 +124,18 @@ input_chars(char *buffer, int &result, int max_size) {
       buffer[result] = '\0';
     }
 
-    if (line_number == 0) {
+    if (x_line_number == 0) {
       // This is a special case.  If we are reading the very first bit
-      // from the stream, copy it into the current_line array.  This
-      // is because the \n.* rule below, which fills current_line
+      // from the stream, copy it into the x_current_line array.  This
+      // is because the \n.* rule below, which fills x_current_line
       // normally, doesn't catch the first line.
-      strncpy(current_line, xyytext, max_error_width);
-      current_line[max_error_width] = '\0';
-      line_number++;
-      col_number = 0;
+      strncpy(x_current_line, xyytext, max_error_width);
+      x_current_line[max_error_width] = '\0';
+      x_line_number++;
+      x_col_number = 0;
 
       // Truncate it at the newline.
-      char *end = strchr(current_line, '\n');
+      char *end = strchr(x_current_line, '\n');
       if (end != NULL) {
         *end = '\0';
       }
@@ -170,11 +176,11 @@ scan_quoted_string(char quote_mark) {
   // occurring at the start of the string, not at the end--somewhat
   // more convenient for the user.
 
-  // Instead of adjusting the global line_number and col_number
+  // Instead of adjusting the global x_line_number and x_col_number
   // variables, we'll operate on our own local variables for the
   // interim.
-  int line = line_number;
-  int col = col_number;
+  int line = x_line_number;
+  int col = x_col_number;
 
   int c;
   c = read_char(line, col);
@@ -274,8 +280,8 @@ scan_quoted_string(char quote_mark) {
     xyyerror("This quotation mark is unterminated.");
   }
 
-  line_number = line;
-  col_number = col;
+  x_line_number = line;
+  x_col_number = col;
 
   return result;
 }
@@ -290,11 +296,11 @@ scan_guid_string() {
   // occurring at the start of the string, not at the end--somewhat
   // more convenient for the user.
 
-  // Instead of adjusting the global line_number and col_number
+  // Instead of adjusting the global x_line_number and x_col_number
   // variables, we'll operate on our own local variables for the
   // interim.
-  int line = line_number;
-  int col = col_number;
+  int line = x_line_number;
+  int col = x_col_number;
 
   int num_digits = 0;
   int num_hyphens = 0;
@@ -311,8 +317,8 @@ scan_guid_string() {
       num_hyphens++;
 
     } else {
-      line_number = line;
-      col_number = col;
+      x_line_number = line;
+      x_col_number = col;
       xyyerror("Invalid character in GUID.");
       return string();
     }
@@ -335,19 +341,25 @@ scan_guid_string() {
     return string();
   }
 
-  line_number = line;
-  col_number = col;
+  x_line_number = line;
+  x_col_number = col;
 
   return result;
 }
 
 // Parses the text into a list of integers and returns them.
 static PTA_int
-parse_int_list(const string &text, const string &delimiter) {
+parse_int_list(const string &text, char delimiter) {
   PTA_int result;
 
   vector_string words;
-  tokenize(text, words, delimiter);
+
+  // The last character of text might be the delimiter.
+  if (!text.empty() && text[text.size() - 1] == delimiter) {
+    tokenize(text.substr(0, text.size() - 1), words, string(1, delimiter));
+  } else {
+    tokenize(text, words, string(1, delimiter));
+  }
 
   vector_string::const_iterator wi;
   for (wi = words.begin(); wi != words.end(); ++wi) {
@@ -361,11 +373,17 @@ parse_int_list(const string &text, const string &delimiter) {
 
 // Parses the text into a list of doubles and returns them.
 static PTA_double
-parse_double_list(const string &text, const string &delimiter) {
+parse_double_list(const string &text, char delimiter) {
   PTA_double result;
 
   vector_string words;
-  tokenize(text, words, delimiter);
+
+  // The last character of text might be the delimiter.
+  if (!text.empty() && text[text.size() - 1] == delimiter) {
+    tokenize(text.substr(0, text.size() - 1), words, string(1, delimiter));
+  } else {
+    tokenize(text, words, string(1, delimiter));
+  }
 
   vector_string::const_iterator wi;
   for (wi = words.begin(); wi != words.end(); ++wi) {
@@ -382,7 +400,7 @@ parse_double_list(const string &text, const string &delimiter) {
 // accept() is called below as each piece is pulled off and
 // accepted by the lexer; it increments the current column number.
 inline void accept() {
-  col_number += yyleng;
+  x_col_number += yyleng;
 }
 
 %}
@@ -399,10 +417,10 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   // New line.  Save a copy of the line so we can print it out for the
   // benefit of the user in case we get an error.
 
-  strncpy(current_line, xyytext+1, max_error_width);
-  current_line[max_error_width] = '\0';
-  line_number++;
-  col_number=0;
+  strncpy(x_current_line, xyytext+1, max_error_width);
+  x_current_line[max_error_width] = '\0';
+  x_line_number++;
+  x_col_number=0;
 
   // Return the whole line to the lexer, except the newline character,
   // which we eat.
@@ -541,7 +559,7 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
 ({INTEGERNUM};)+ { 
   // An integer as part of a semicolon-delimited list.
   accept();
-  xyylval.int_list = parse_int_list(xyytext, ";");
+  xyylval.int_list = parse_int_list(xyytext, ';');
   xyylval.u.separator = ';';
 
   return TOKEN_INTEGER_LIST;
@@ -550,25 +568,43 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
 ({REALNUM};)+ { 
   // A floating-point number as part of a semicolon-delimited list.
   accept(); 
-  xyylval.double_list = parse_double_list(xyytext, ";");
+  xyylval.double_list = parse_double_list(xyytext, ';');
   xyylval.u.separator = ';';
 
   return TOKEN_REALNUM_LIST;
 }
 
 ({INTEGERNUM},)+ { 
-  // An integer as part of a semicolon-delimited list.
+  // An integer as part of a comma-delimited list.
   accept();
-  xyylval.int_list = parse_int_list(xyytext, ",");
+  xyylval.int_list = parse_int_list(xyytext, ',');
   xyylval.u.separator = ',';
 
   return TOKEN_INTEGER_LIST;
 }
 
 ({REALNUM},)+ { 
-  // A floating-point number as part of a semicolon-delimited list.
+  // A floating-point number as part of a comma-delimited list.
+  accept(); 
+  xyylval.double_list = parse_double_list(xyytext, ',');
+  xyylval.u.separator = ',';
+
+  return TOKEN_REALNUM_LIST;
+}
+
+({INTEGERNUM},)+{INTEGERNUM} { 
+  // An integer as part of a comma-delimited list.
+  accept();
+  xyylval.int_list = parse_int_list(xyytext, ',');
+  xyylval.u.separator = ',';
+
+  return TOKEN_INTEGER_LIST;
+}
+
+({REALNUM},)+{REALNUM} { 
+  // A floating-point number as part of a comma-delimited list.
   accept(); 
-  xyylval.double_list = parse_double_list(xyytext, ",");
+  xyylval.double_list = parse_double_list(xyytext, ',');
   xyylval.u.separator = ',';
 
   return TOKEN_REALNUM_LIST;

+ 6 - 0
pandatool/src/xfile/xLexerDefs.h

@@ -26,8 +26,14 @@ int x_error_count();
 int x_warning_count();
 
 void xyyerror(const string &msg);
+void xyyerror(const string &msg, int line_number, int col_number, 
+              const string &current_line);
 void xyywarning(const string &msg);
 
 int xyylex();
 
+extern int x_line_number;
+extern int x_col_number;
+extern char x_current_line[];
+
 #endif

+ 5 - 23
pandatool/src/xfile/xParser.yxx

@@ -349,9 +349,7 @@ object:
 {
   XFileDataObjectTemplate *current_template = 
     DCAST(XFileDataObjectTemplate, current_node);
-  if (!current_template->finalize_parse_data()) {
-    yyerror("Invalid data for object.");
-  }
+  current_template->finalize_parse_data();
 
   $$ = current_node;
   current_node = $<u.node>4;
@@ -366,27 +364,11 @@ data_parts_list:
 data_part:
         TOKEN_OBRACE data_reference TOKEN_CBRACE
 {
-  if ($2 != (XFileNode *)NULL) {
-    if (!$2->is_of_type(XFileDataObjectTemplate::get_class_type())) {
-      // Actually, maybe you can--the docs aren't clear about this.
-      // But I don't think there's any real reason to.
-      yyerror("Can't reference primitive data type.");
-    } else {
-      XFileDataObjectTemplate *object = 
-        DCAST(XFileDataObjectTemplate, $2);
-      XFileDataObjectTemplate *current_template = 
-        DCAST(XFileDataObjectTemplate, current_node);
-      current_template->add_parse_object(object, true);
-    }
-  }
+  // nested references should be added as children too.
 }
 	| object
 {
-  XFileDataObjectTemplate *object = 
-    DCAST(XFileDataObjectTemplate, $1);
-  XFileDataObjectTemplate *current_template = 
-    DCAST(XFileDataObjectTemplate, current_node);
-  current_template->add_parse_object(object, false);
+  // nested objects are just quietly added as children.
 }
         | integer_list
 {
@@ -440,7 +422,7 @@ list_separator:
 data_reference:
         name
 {
-  XFileDataObject *data_object = x_file->find_data_object($1);
+  XFileDataObjectTemplate *data_object = x_file->find_data_object($1);
   if (data_object == (XFileDataObject *)NULL) {
     yyerror("Unknown data_object: " + $1);
   }
@@ -449,7 +431,7 @@ data_reference:
 }
 	| name class_id
 {
-  XFileDataObject *data_object = x_file->find_data_object($2);
+  XFileDataObjectTemplate *data_object = x_file->find_data_object($2);
   if (data_object == (XFileDataObject *)NULL) {
     yyerror("Unknown data_object: " + $1);
   } else {