Browse Source

add break and default to DCSwitch

David Rose 20 years ago
parent
commit
62eae82c23

+ 10 - 0
direct/src/dcparser/dcLexer.lxx

@@ -479,6 +479,16 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   return KW_CASE;
   return KW_CASE;
 }
 }
 
 
+"default" {
+  accept();
+  return KW_DEFAULT;
+}
+
+"break" {
+  accept();
+  return KW_BREAK;
+}
+
 "int8" {
 "int8" {
   accept();
   accept();
   return KW_INT8;
   return KW_INT8;

+ 25 - 2
direct/src/dcparser/dcParser.yxx

@@ -91,6 +91,8 @@ dc_cleanup_parser() {
 %token KW_TYPEDEF 
 %token KW_TYPEDEF 
 %token KW_SWITCH
 %token KW_SWITCH
 %token KW_CASE
 %token KW_CASE
+%token KW_DEFAULT
+%token KW_BREAK
 
 
 %token KW_INT8
 %token KW_INT8
 %token KW_INT16
 %token KW_INT16
@@ -1226,9 +1228,11 @@ switch_fields:
         empty
         empty
         | switch_fields ';'
         | switch_fields ';'
         | switch_fields switch_case
         | switch_fields switch_case
+        | switch_fields switch_default
+        | switch_fields switch_break
         | switch_fields switch_field
         | switch_fields switch_field
 {
 {
-  if (current_switch->get_num_cases() == 0) {
+  if (!current_switch->is_field_valid()) {
     yyerror("case declaration required before first element");
     yyerror("case declaration required before first element");
   } else if ($2 != (DCField *)NULL) {
   } else if ($2 != (DCField *)NULL) {
     if (!current_switch->add_field($2)) {
     if (!current_switch->add_field($2)) {
@@ -1250,11 +1254,30 @@ switch_case:
   if (!current_packer->end_pack()) {
   if (!current_packer->end_pack()) {
     yyerror("Invalid value for switch parameter");
     yyerror("Invalid value for switch parameter");
   } else {
   } else {
-    current_switch->add_case(current_packer->get_string());
+    int case_index = current_switch->add_case(current_packer->get_string());
+    if (case_index == -1) {
+      yyerror("Duplicate case value");
+    }
+  }
+}
+	;
+
+switch_default:
+	KW_DEFAULT ':'
+{
+  if (!current_switch->add_default()) {
+    yyerror("Default case already defined");
   }
   }
 }
 }
 	;
 	;
 
 
+switch_break:
+	KW_BREAK ';'
+{
+  current_switch->add_break();
+}
+	;
+
 switch_field:
 switch_field:
 	unnamed_parameter_with_default ';'
 	unnamed_parameter_with_default ';'
 {
 {

+ 304 - 94
direct/src/dcparser/dcSwitch.cxx

@@ -35,6 +35,8 @@ DCSwitch(const string &name, DCParameter *key_parameter) :
   _name(name),
   _name(name),
   _key_parameter(key_parameter)
   _key_parameter(key_parameter)
 {
 {
+  _default_case = NULL;
+  _fields_added = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -44,13 +46,26 @@ DCSwitch(const string &name, DCParameter *key_parameter) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DCSwitch::
 DCSwitch::
 ~DCSwitch() {
 ~DCSwitch() {
+  nassertv(_key_parameter != (DCParameter *)NULL);
   delete _key_parameter;
   delete _key_parameter;
 
 
   Cases::iterator ci;
   Cases::iterator ci;
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
-    const SwitchCase *dcase = (*ci);
+    SwitchCase *dcase = (*ci);
     delete dcase;
     delete dcase;
   }
   }
+
+  CaseFields::iterator fi;
+  for (fi = _case_fields.begin(); fi != _case_fields.end(); ++fi) {
+    SwitchFields *fields = (*fi);
+    delete fields;
+  }
+
+  Fields::iterator ni;
+  for (ni = _nested_fields.begin(); ni != _nested_fields.end(); ++ni) {
+    DCField *field = (*ni);
+    delete field;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -134,7 +149,7 @@ get_case_by_value(const string &case_value) const {
 DCPackerInterface *DCSwitch::
 DCPackerInterface *DCSwitch::
 get_case(int n) const {
 get_case(int n) const {
   nassertr(n >= 0 && n < (int)_cases.size(), NULL);
   nassertr(n >= 0 && n < (int)_cases.size(), NULL);
-  return _cases[n];
+  return _cases[n]->_fields;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -157,7 +172,7 @@ get_value(int case_index) const {
 int DCSwitch::
 int DCSwitch::
 get_num_fields(int case_index) const {
 get_num_fields(int case_index) const {
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), 0);
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), 0);
-  return _cases[case_index]->_fields.size();
+  return _cases[case_index]->_fields->_fields.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -168,8 +183,8 @@ get_num_fields(int case_index) const {
 DCField *DCSwitch::
 DCField *DCSwitch::
 get_field(int case_index, int n) const {
 get_field(int case_index, int n) const {
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
-  nassertr(n >= 0 && n < (int)_cases[case_index]->_fields.size(), NULL);
-  return _cases[case_index]->_fields[n];
+  nassertr(n >= 0 && n < (int)_cases[case_index]->_fields->_fields.size(), NULL);
+  return _cases[case_index]->_fields->_fields[n];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -182,7 +197,7 @@ DCField *DCSwitch::
 get_field_by_name(int case_index, const string &name) const {
 get_field_by_name(int case_index, const string &name) const {
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
   nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
 
 
-  const FieldsByName &fields_by_name = _cases[case_index]->_fields_by_name;
+  const FieldsByName &fields_by_name = _cases[case_index]->_fields->_fields_by_name;
   FieldsByName::const_iterator ni;
   FieldsByName::const_iterator ni;
   ni = fields_by_name.find(name);
   ni = fields_by_name.find(name);
   if (ni != fields_by_name.end()) {
   if (ni != fields_by_name.end()) {
@@ -192,6 +207,18 @@ get_field_by_name(int case_index, const string &name) const {
   return NULL;
   return NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::is_field_valid
+//       Access: Public
+//  Description: Returns true if it is valid to add a new field at
+//               this point (implying that a case or default has been
+//               added already), or false if not.
+////////////////////////////////////////////////////////////////////
+bool DCSwitch::
+is_field_valid() const {
+  return !_current_fields.empty();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DCSwitch::add_case
 //     Function: DCSwitch::add_case
 //       Access: Public
 //       Access: Public
@@ -207,30 +234,71 @@ add_case(const string &value) {
     return -1;
     return -1;
   }
   }
 
 
-  SwitchCase *dcase = new SwitchCase(_name, value);
-  dcase->add_field(_key_parameter);
+  SwitchFields *fields = start_new_case();
+  SwitchCase *dcase = new SwitchCase(value, fields);
   _cases.push_back(dcase);
   _cases.push_back(dcase);
   return case_index;
   return case_index;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::add_default
+//       Access: Public
+//  Description: Adds a default case to the switch.  Returns true if
+//               the case is successfully added, or false if it had
+//               already been added.  This is normally called only by
+//               the parser.
+////////////////////////////////////////////////////////////////////
+bool DCSwitch::
+add_default() {
+  if (_default_case != (SwitchFields *)NULL) {
+    return false;
+  }
+
+  _default_case = start_new_case();
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DCSwitch::add_field
 //     Function: DCSwitch::add_field
 //       Access: Public
 //       Access: Public
-//  Description: Adds a field to the case most recently added via
-//               add_case().  Returns true if successful, false if the
-//               field duplicates a field already named within this
-//               case.  It is an error to call this before calling
-//               add_case().  This is normally called only by the
-//               parser.
+//  Description: Adds a field to the currently active cases (those
+//               that have been added via add_case() or add_default(),
+//               since the last call to add_break()).  Returns true if
+//               successful, false if the field duplicates a field
+//               already named within this case.  It is an error to
+//               call this before calling add_case() or add_default().
+//               This is normally called only by the parser.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool DCSwitch::
 bool DCSwitch::
 add_field(DCField *field) {
 add_field(DCField *field) {
-  nassertr(!_cases.empty(), false);
+  nassertr(!_current_fields.empty(), false);
 
 
-  if (!_cases.back()->add_field(field)) {
-    return false;
+  bool all_ok = true;
+
+  CaseFields::iterator fi;
+  for (fi = _current_fields.begin(); fi != _current_fields.end(); ++fi) {
+    SwitchFields *fields = (*fi);
+    if (!fields->add_field(field)) {
+      all_ok = false;
+    }
   }
   }
-  return true;
+  _nested_fields.push_back(field);
+  _fields_added = true;
+
+  return all_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::add_break
+//       Access: Public
+//  Description: Adds a break statement to the switch.  This closes
+//               the currently open cases and prepares for a new,
+//               unrelated case.
+////////////////////////////////////////////////////////////////////
+void DCSwitch::
+add_break() {
+  _current_fields.clear();
+  _fields_added = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -246,10 +314,15 @@ apply_switch(const char *value_data, size_t length) const {
   CasesByValue::const_iterator vi;
   CasesByValue::const_iterator vi;
   vi = _cases_by_value.find(string(value_data, length));
   vi = _cases_by_value.find(string(value_data, length));
   if (vi != _cases_by_value.end()) {
   if (vi != _cases_by_value.end()) {
-    return _cases[(*vi).second];
+    return _cases[(*vi).second]->_fields;
   }
   }
 
 
-  // Invalid value.
+  // Unexpected value--use the default.
+  if (_default_case != (SwitchFields *)NULL) {
+    return _default_case;
+  }
+
+  // No default.
   return NULL;
   return NULL;
 }
 }
 
 
@@ -292,22 +365,29 @@ output_instance(ostream &out, bool brief, const string &prename,
   _key_parameter->output(out, brief);
   _key_parameter->output(out, brief);
   out << ") {";
   out << ") {";
 
 
+  const SwitchFields *last_fields = NULL;
+
   Cases::const_iterator ci;
   Cases::const_iterator ci;
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
     const SwitchCase *dcase = (*ci);
     const SwitchCase *dcase = (*ci);
+    if (dcase->_fields != last_fields && last_fields != (SwitchFields *)NULL) {
+      last_fields->output(out, brief);
+    }
+    last_fields = dcase->_fields;
     out << "case " << _key_parameter->format_data(dcase->_value) << ": ";
     out << "case " << _key_parameter->format_data(dcase->_value) << ": ";
-    
-    Fields::const_iterator fi;
-    if (!dcase->_fields.empty()) {
-      fi = dcase->_fields.begin();
-      ++fi;
-      while (fi != dcase->_fields.end()) {
-        (*fi)->output(out, brief);
-        out << "; ";
-        ++fi;
-      }
+  }
+
+  if (_default_case != (SwitchFields *)NULL) {
+    if (_default_case != last_fields && last_fields != (SwitchFields *)NULL) {
+      last_fields->output(out, brief);
     }
     }
+    last_fields = _default_case;
+    out << "default: ";
+  }
+  if (last_fields != (SwitchFields *)NULL) {
+    last_fields->output(out, brief);
   }
   }
+
   out << "}";
   out << "}";
   if (!prename.empty() || !name.empty() || !postname.empty()) {
   if (!prename.empty() || !name.empty() || !postname.empty()) {
     out << " " << prename << name << postname;
     out << " " << prename << name << postname;
@@ -333,22 +413,31 @@ write_instance(ostream &out, bool brief, int indent_level,
   _key_parameter->output(out, brief);
   _key_parameter->output(out, brief);
   out << ") {\n";
   out << ") {\n";
 
 
+  const SwitchFields *last_fields = NULL;
+
   Cases::const_iterator ci;
   Cases::const_iterator ci;
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
     const SwitchCase *dcase = (*ci);
     const SwitchCase *dcase = (*ci);
+    if (dcase->_fields != last_fields && last_fields != (SwitchFields *)NULL) {
+      last_fields->write(out, brief, indent_level + 2);
+    }
+    last_fields = dcase->_fields;
     indent(out, indent_level)
     indent(out, indent_level)
       << "case " << _key_parameter->format_data(dcase->_value) << ":\n";
       << "case " << _key_parameter->format_data(dcase->_value) << ":\n";
-    
-    Fields::const_iterator fi;
-    if (!dcase->_fields.empty()) {
-      fi = dcase->_fields.begin();
-      ++fi;
-      while (fi != dcase->_fields.end()) {
-        (*fi)->write(out, brief, indent_level + 2);
-        ++fi;
-      }
+  }
+
+  if (_default_case != (SwitchFields *)NULL) {
+    if (_default_case != last_fields && last_fields != (SwitchFields *)NULL) {
+      last_fields->write(out, brief, indent_level + 2);
     }
     }
+    last_fields = _default_case;
+    indent(out, indent_level)
+      << "default:\n";
   }
   }
+  if (last_fields != (SwitchFields *)NULL) {
+    last_fields->write(out, brief, indent_level + 2);
+  }
+
   indent(out, indent_level)
   indent(out, indent_level)
     << "}";
     << "}";
   if (!prename.empty() || !name.empty() || !postname.empty()) {
   if (!prename.empty() || !name.empty() || !postname.empty()) {
@@ -375,9 +464,19 @@ generate_hash(HashGenerator &hashgen) const {
     const SwitchCase *dcase = (*ci);
     const SwitchCase *dcase = (*ci);
     hashgen.add_string(dcase->_value);
     hashgen.add_string(dcase->_value);
 
 
-    hashgen.add_int(dcase->_fields.size());
+    const SwitchFields *fields = dcase->_fields;
+    hashgen.add_int(fields->_fields.size());
     Fields::const_iterator fi;
     Fields::const_iterator fi;
-    for (fi = dcase->_fields.begin(); fi != dcase->_fields.end(); ++fi) {
+    for (fi = fields->_fields.begin(); fi != fields->_fields.end(); ++fi) {
+      (*fi)->generate_hash(hashgen);
+    }
+  }
+
+  if (_default_case != (SwitchFields *)NULL) {
+    const SwitchFields *fields = _default_case;
+    hashgen.add_int(fields->_fields.size());
+    Fields::const_iterator fi;
+    for (fi = fields->_fields.begin(); fi != fields->_fields.end(); ++fi) {
       (*fi)->generate_hash(hashgen);
       (*fi)->generate_hash(hashgen);
     }
     }
   }
   }
@@ -386,36 +485,49 @@ generate_hash(HashGenerator &hashgen) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DCSwitch::pack_default_value
 //     Function: DCSwitch::pack_default_value
 //       Access: Public
 //       Access: Public
-//  Description: Packs the switchParameter's specified default value (or a
-//               sensible default if no value is specified) into the
-//               stream.  Returns true if the default value is packed,
-//               false if the switchParameter doesn't know how to pack its
-//               default value.
+//  Description: Packs the switchParameter's specified default value
+//               (or a sensible default if no value is specified) into
+//               the stream.  Returns true if the default value is
+//               packed, false if the switchParameter doesn't know how
+//               to pack its default value.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool DCSwitch::
 bool DCSwitch::
 pack_default_value(DCPackData &pack_data, bool &pack_error) const {
 pack_default_value(DCPackData &pack_data, bool &pack_error) const {
-  if (_cases.empty()) {
-    pack_error = true;
-    return true;
-  }
-
-  // The default value for a switch is the first case.
-
+  SwitchFields *fields = NULL;
   DCPacker packer;
   DCPacker packer;
-  packer.begin_pack(_cases[0]);
-  packer.pack_literal_value(_cases[0]->_value);
+  packer.begin_pack(_key_parameter);
+  if (!_cases.empty()) {
+    // If we have any cases, the first case is always the default
+    // case, regardless of the default value specified by the key
+    // parameter.  That's just the easiest to code.
+    packer.pack_literal_value(_cases[0]->_value);
+    fields = _cases[0]->_fields;
+
+  } else {
+    // If we don't have any cases, just pack the key parameter's
+    // default.
+    packer.pack_default_value();
+    fields = _default_case;
+  }    
+
   if (!packer.end_pack()) {
   if (!packer.end_pack()) {
     pack_error = true;
     pack_error = true;
   }
   }
 
 
-  // Then everything within the case gets its normal default.
-  for (size_t i = 1; i < _cases[0]->_fields.size(); i++) {
-    packer.begin_pack(_cases[0]->_fields[i]);
-    packer.pack_default_value();
-    if (!packer.end_pack()) {
-      pack_error = true;
+  if (fields == (SwitchFields *)NULL) {
+    pack_error = true;
+
+  } else {
+    // Then everything within the case gets its normal default.
+    for (size_t i = 1; i < fields->_fields.size(); i++) {
+      packer.begin_pack(fields->_fields[i]);
+      packer.pack_default_value();
+      if (!packer.end_pack()) {
+        pack_error = true;
+      }
     }
     }
   }
   }
+
   pack_data.append_data(packer.get_data(), packer.get_length());
   pack_data.append_data(packer.get_data(), packer.get_length());
 
 
   return true;
   return true;
@@ -425,8 +537,9 @@ pack_default_value(DCPackData &pack_data, bool &pack_error) const {
 //     Function: DCSwitch::do_check_match_switch
 //     Function: DCSwitch::do_check_match_switch
 //       Access: Public
 //       Access: Public
 //  Description: Returns true if this switch matches the indicated
 //  Description: Returns true if this switch matches the indicated
-//               switch, false otherwise.  This is only intended to be
-//               called internally from
+//               other switch--that is, the two switches are bitwise
+//               equivalent--false otherwise.  This is only intended
+//               to be called internally from
 //               DCSwitchParameter::do_check_match_switch_parameter().
 //               DCSwitchParameter::do_check_match_switch_parameter().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool DCSwitch::
 bool DCSwitch::
@@ -461,14 +574,47 @@ do_check_match_switch(const DCSwitch *other) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::Constructor
+//     Function: DCSwitch::start_new_case
+//       Access: Private
+//  Description: Creates a new field set for the new case, or shares
+//               the field set with the previous case, as appropriate.
+//               Returns the appropriate field set.
+////////////////////////////////////////////////////////////////////
+DCSwitch::SwitchFields *DCSwitch::
+start_new_case() {
+  SwitchFields *fields = NULL;
+
+  if (_current_fields.empty() || _fields_added) {
+    // If we have recently encountered a break (which removes all of
+    // the current field sets) or if we have already added at least
+    // one field to the previous case without an intervening break,
+    // then we can't share the field set with the previous case.
+    // Create a new one.
+    fields = new SwitchFields(_name);
+    fields->add_field(_key_parameter);
+
+    _case_fields.push_back(fields);
+    _current_fields.push_back(fields);
+
+  } else {
+    // Otherwise, we can share the field set with the previous case.
+    fields = _current_fields.back();
+  }
+
+  _fields_added = false;
+
+  return fields;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchFields::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-DCSwitch::SwitchCase::
-SwitchCase(const string &name, const string &value) :
-  DCPackerInterface(name),
-  _value(value)
+DCSwitch::SwitchFields::
+SwitchFields(const string &name) :
+  DCPackerInterface(name)
 {
 {
   _has_nested_fields = true;
   _has_nested_fields = true;
   _num_nested_fields = 0;
   _num_nested_fields = 0;
@@ -482,50 +628,40 @@ SwitchCase(const string &name, const string &value) :
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::Destructor
+//     Function: DCSwitch::SwitchFields::Destructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-DCSwitch::SwitchCase::
-~SwitchCase() {
-  Fields::iterator fi = _fields.begin();
-  
-  // Be careful not to delete the _key_parameter, which is added to
-  // the beginning of each case.
-  nassertv(fi != _fields.end());
-  ++fi;
-
-  // But we do want to delete all of the other fields.
-  while (fi != _fields.end()) {
-    delete (*fi);
-    ++fi;
-  }
-  _fields.clear();
+DCSwitch::SwitchFields::
+~SwitchFields() {
+  // We don't delete any of the nested fields here, since they might
+  // be shared by multiple SwitchFields objects.  Instead, we delete
+  // them in the DCSwitch destructor.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::get_nested_field
+//     Function: DCSwitch::SwitchFields::get_nested_field
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Returns the DCPackerInterface object that represents
 //  Description: Returns the DCPackerInterface object that represents
 //               the nth nested field.  This may return NULL if there
 //               the nth nested field.  This may return NULL if there
 //               is no such field (but it shouldn't do this if n is in
 //               is no such field (but it shouldn't do this if n is in
 //               the range 0 <= n < get_num_nested_fields()).
 //               the range 0 <= n < get_num_nested_fields()).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-DCPackerInterface *DCSwitch::SwitchCase::
+DCPackerInterface *DCSwitch::SwitchFields::
 get_nested_field(int n) const {
 get_nested_field(int n) const {
   nassertr(n >= 0 && n < (int)_fields.size(), NULL);
   nassertr(n >= 0 && n < (int)_fields.size(), NULL);
   return _fields[n];
   return _fields[n];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::add_field
+//     Function: DCSwitch::SwitchFields::add_field
 //       Access: Public
 //       Access: Public
 //  Description: Adds a field to this case.  Returns true if
 //  Description: Adds a field to this case.  Returns true if
 //               successful, false if the field duplicates a field
 //               successful, false if the field duplicates a field
 //               already named within this case.  This is normally
 //               already named within this case.  This is normally
 //               called only by the parser.
 //               called only by the parser.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool DCSwitch::SwitchCase::
+bool DCSwitch::SwitchFields::
 add_field(DCField *field) {
 add_field(DCField *field) {
   if (!field->get_name().empty()) {
   if (!field->get_name().empty()) {
     bool inserted = _fields_by_name.insert
     bool inserted = _fields_by_name.insert
@@ -558,15 +694,15 @@ add_field(DCField *field) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::do_check_match_switch_case
+//     Function: DCSwitch::SwitchFields::do_check_match_switch_case
 //       Access: Public
 //       Access: Public
 //  Description: Returns true if this case matches the indicated
 //  Description: Returns true if this case matches the indicated
 //               case, false otherwise.  This is only intended to be
 //               case, false otherwise.  This is only intended to be
 //               called internally from
 //               called internally from
 //               DCSwitch::do_check_match_switch().
 //               DCSwitch::do_check_match_switch().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool DCSwitch::SwitchCase::
-do_check_match_switch_case(const DCSwitch::SwitchCase *other) const {
+bool DCSwitch::SwitchFields::
+do_check_match_switch_case(const DCSwitch::SwitchFields *other) const {
   if (_fields.size() != other->_fields.size()) {
   if (_fields.size() != other->_fields.size()) {
     return false;
     return false;
   }
   }
@@ -578,18 +714,92 @@ do_check_match_switch_case(const DCSwitch::SwitchCase *other) const {
 
 
   return true;
   return true;
 }
 }
+    
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchFields::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void DCSwitch::SwitchFields::
+output(ostream &out, bool brief) const {
+  Fields::const_iterator fi;
+  if (!_fields.empty()) {
+    fi = _fields.begin();
+    ++fi;
+    while (fi != _fields.end()) {
+      (*fi)->output(out, brief);
+      out << "; ";
+      ++fi;
+    }
+  }
+  out << "break; ";
+}
+    
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchFields::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void DCSwitch::SwitchFields::
+write(ostream &out, bool brief, int indent_level) const {
+  Fields::const_iterator fi;
+  if (!_fields.empty()) {
+    fi = _fields.begin();
+    ++fi;
+    while (fi != _fields.end()) {
+      (*fi)->write(out, brief, indent_level);
+      ++fi;
+    }
+  }
+  indent(out, indent_level)
+    << "break;\n";
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: DCSwitch::SwitchCase::do_check_match
+//     Function: DCSwitch::SwitchFields::do_check_match
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description: Returns true if the other interface is bitwise the
 //  Description: Returns true if the other interface is bitwise the
 //               same as this one--that is, a uint32 only matches a
 //               same as this one--that is, a uint32 only matches a
 //               uint32, etc. Names of components, and range limits,
 //               uint32, etc. Names of components, and range limits,
 //               are not compared.
 //               are not compared.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool DCSwitch::SwitchCase::
+bool DCSwitch::SwitchFields::
 do_check_match(const DCPackerInterface *) const {
 do_check_match(const DCPackerInterface *) const {
-  // This should never be called on a SwitchCase.
+  // This should never be called on a SwitchFields.
   nassertr(false, false);
   nassertr(false, false);
   return false;
   return false;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch::SwitchCase::
+SwitchCase(const string &value, DCSwitch::SwitchFields *fields) :
+  _value(value),
+  _fields(fields)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCSwitch::SwitchCase::
+~SwitchCase() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCSwitch::SwitchCase::do_check_match_switch_case
+//       Access: Public
+//  Description: Returns true if this case matches the indicated
+//               case, false otherwise.  This is only intended to be
+//               called internally from
+//               DCSwitch::do_check_match_switch().
+////////////////////////////////////////////////////////////////////
+bool DCSwitch::SwitchCase::
+do_check_match_switch_case(const DCSwitch::SwitchCase *other) const {
+  return _fields->do_check_match_switch_case(other->_fields);
+}

+ 45 - 6
direct/src/dcparser/dcSwitch.h

@@ -56,8 +56,11 @@ PUBLISHED:
   DCField *get_field_by_name(int case_index, const string &name) const;
   DCField *get_field_by_name(int case_index, const string &name) const;
 
 
 public:
 public:
+  bool is_field_valid() const;
   int add_case(const string &value);
   int add_case(const string &value);
+  bool add_default();
   bool add_field(DCField *field);
   bool add_field(DCField *field);
+  void add_break();
 
 
   const DCPackerInterface *apply_switch(const char *value_data, size_t length) const;
   const DCPackerInterface *apply_switch(const char *value_data, size_t length) const;
 
 
@@ -77,32 +80,68 @@ public:
   typedef pvector<DCField *> Fields;
   typedef pvector<DCField *> Fields;
   typedef pmap<string, DCField *> FieldsByName;
   typedef pmap<string, DCField *> FieldsByName;
 
 
-  class SwitchCase : public DCPackerInterface {
+  class SwitchFields : public DCPackerInterface {
   public:
   public:
-    SwitchCase(const string &name, const string &value);
-    ~SwitchCase();
+    SwitchFields(const string &name);
+    ~SwitchFields();
     virtual DCPackerInterface *get_nested_field(int n) const;
     virtual DCPackerInterface *get_nested_field(int n) const;
 
 
     bool add_field(DCField *field);
     bool add_field(DCField *field);
-    bool do_check_match_switch_case(const SwitchCase *other) const;
+    bool do_check_match_switch_case(const SwitchFields *other) const;
+
+    void output(ostream &out, bool brief) const;
+    void write(ostream &out, bool brief, int indent_level) const;
 
 
   protected:
   protected:
     virtual bool do_check_match(const DCPackerInterface *other) const;
     virtual bool do_check_match(const DCPackerInterface *other) const;
 
 
   public:
   public:
-    string _value;
     Fields _fields;
     Fields _fields;
     FieldsByName _fields_by_name;
     FieldsByName _fields_by_name;
     bool _has_default_value;
     bool _has_default_value;
   };
   };
 
 
+  class SwitchCase {
+  public:
+    SwitchCase(const string &value, SwitchFields *fields);
+    ~SwitchCase();
+
+    bool do_check_match_switch_case(const SwitchCase *other) const;
+
+  public:
+    string _value;
+    SwitchFields *_fields;
+  };
+
+private:
+  SwitchFields *start_new_case();
+
 private:
 private:
   string _name;
   string _name;
   DCParameter *_key_parameter;
   DCParameter *_key_parameter;
 
 
   typedef pvector<SwitchCase *> Cases;
   typedef pvector<SwitchCase *> Cases;
   Cases _cases;
   Cases _cases;
-
+  SwitchFields *_default_case;
+
+  // All SwitchFields created and used by the DCSwitch object are also
+  // stored here; this is the vector that "owns" the pointers.
+  typedef pvector<SwitchFields *> CaseFields;
+  CaseFields _case_fields;
+
+  // All nested DCField objects that have been added to one or more of
+  // the above SwitchFields are also recorded here; this is the vector
+  // that "owns" these pointers.
+  Fields _nested_fields;
+
+  // These are the SwitchFields that are currently being filled up
+  // during this stage of the parser.  There might be more than one at
+  // a time, if we have multiple cases being introduced in the middle
+  // of a series of fields (without a break statement intervening).
+  CaseFields _current_fields;
+  bool _fields_added;
+
+  // This map indexes into the _cases vector, above.
   typedef pmap<string, int> CasesByValue;
   typedef pmap<string, int> CasesByValue;
   CasesByValue _cases_by_value;
   CasesByValue _cases_by_value;
 };
 };

+ 7 - 5
direct/src/dcparser/dcSwitchParameter.cxx

@@ -54,16 +54,18 @@ DCSwitchParameter(const DCSwitch *dswitch) :
     _fixed_byte_size = _dswitch->get_case(0)->get_fixed_byte_size();
     _fixed_byte_size = _dswitch->get_case(0)->get_fixed_byte_size();
     
     
     for (int i = 0; i < num_cases; i++) {
     for (int i = 0; i < num_cases; i++) {
-      const DCSwitch::SwitchCase *dcase = (const DCSwitch::SwitchCase *)_dswitch->get_case(i);
-      if (!dcase->has_fixed_byte_size() || 
-          dcase->get_fixed_byte_size() != _fixed_byte_size) {
+      const DCSwitch::SwitchFields *fields = 
+        (const DCSwitch::SwitchFields *)_dswitch->get_case(i);
+                                     
+      if (!fields->has_fixed_byte_size() || 
+          fields->get_fixed_byte_size() != _fixed_byte_size) {
         
         
         // Nope, we have a variable byte size.
         // Nope, we have a variable byte size.
         _has_fixed_byte_size = false;
         _has_fixed_byte_size = false;
       }
       }
 
 
-      _has_range_limits = _has_range_limits || dcase->has_range_limits();
-      _has_default_value = _has_default_value || dcase->_has_default_value;
+      _has_range_limits = _has_range_limits || fields->has_range_limits();
+      _has_default_value = _has_default_value || fields->_has_default_value;
     }
     }
   }
   }
 
 

+ 2 - 0
direct/src/doc/sample.dc

@@ -105,6 +105,7 @@ struct AvatarDNA {
     DNAColor shirtColor;
     DNAColor shirtColor;
     uint8(0-25) skirtIndex;
     uint8(0-25) skirtIndex;
     DNAColor skirtColor;
     DNAColor skirtColor;
+    break;
 
 
   case 0:
   case 0:
     // Boy clothes
     // Boy clothes
@@ -112,6 +113,7 @@ struct AvatarDNA {
     DNAColor shirtColor;
     DNAColor shirtColor;
     uint8(0-15) shortsIndex;
     uint8(0-15) shortsIndex;
     DNAColor shortsColor;
     DNAColor shortsColor;
+    break;
   };
   };
 
 
   // Nested structure references.
   // Nested structure references.