diff --git a/direct/src/dcparse/test.dc b/direct/src/dcparse/test.dc index 1a4d79a1df..83277cd802 100644 --- a/direct/src/dcparse/test.dc +++ b/direct/src/dcparse/test.dc @@ -3,8 +3,8 @@ dclass Movable { // Declare a few atomic fields - set_xyz(int16 / 1000, int16 / 1000, int16 / 1000) required broadcast ram; - set_hpr(int16, int16, int16) required broadcast ram; + set_xyz(int16 x = 1024 / 1000, int16 y / 1000 = 1.024, int16 z / 1000 = 0.512) required broadcast ram; + set_hpr(int16 h, int16 p , int16 r = 0) required broadcast ram; // And a molecular field set_xyzhpr : set_xyz, set_hpr; @@ -14,7 +14,7 @@ dclass Movable { dclass Attitude : Movable { // methods are inherited from parent class. - set_attitude(int8) required broadcast ram; + set_attitude(int16array attitude = { 1 * 3 }) required broadcast ram; // This molecular field uses some inherited atomic fields. set_xyzhprattitude : set_xyz, set_hpr, set_attitude; diff --git a/direct/src/dcparser/Sources.pp b/direct/src/dcparser/Sources.pp index ef2082f0f9..1a80384783 100644 --- a/direct/src/dcparser/Sources.pp +++ b/direct/src/dcparser/Sources.pp @@ -6,6 +6,7 @@ directbase #define YACC_PREFIX dcyy #define C++FLAGS -DWITHIN_PANDA +#define UNIX_SYS_LIBS m #begin lib_target #define TARGET dcparser diff --git a/direct/src/dcparser/dcAtomicField.cxx b/direct/src/dcparser/dcAtomicField.cxx index 598f50eea4..01662ccab7 100644 --- a/direct/src/dcparser/dcAtomicField.cxx +++ b/direct/src/dcparser/dcAtomicField.cxx @@ -7,6 +7,7 @@ #include "hashGenerator.h" #include "dcindent.h" +#include ostream & operator << (ostream &out, const DCAtomicField::ElementType &et) { @@ -14,9 +15,281 @@ operator << (ostream &out, const DCAtomicField::ElementType &et) { if (et._divisor != 1) { out << " / " << et._divisor; } + if (!et._name.empty()) { + out << " " << et._name; + } + if (et._has_default_value) { + out << " = <" << hex; + string::const_iterator si; + for (si = et._default_value.begin(); si != et._default_value.end(); ++si) { + out << setw(2) << setfill('0') << (int)(unsigned char)(*si); + } + out << dec << ">"; + } return out; } +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DCAtomicField::ElementType:: +ElementType() { + _type = ST_invalid; + _divisor = 1; + _has_default_value = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::set_default_value +// Access: Public +// Description: Stores the indicated value as the default value for +// this element. +// +// Returns true if the element type reasonably accepts a +// default value of numeric type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +set_default_value(double num) { + switch (_type) { + case ST_int16array: + case ST_uint16array: + case ST_int32array: + case ST_uint32array: + case ST_blob: + // These array types don't take numbers. + return false; + default: + break; + } + + string formatted; + if (!format_default_value(num, formatted)) { + return false; + } + + _default_value = formatted; + _has_default_value = true; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::set_default_value +// Access: Public +// Description: Stores the indicated value as the default value for +// this element. +// +// Returns true if the element type reasonably accepts a +// default value of string type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +set_default_value(const string &str) { + switch (_type) { + case ST_int16array: + case ST_uint16array: + case ST_int32array: + case ST_uint32array: + // These array types don't take strings. + return false; + default: + break; + } + + string formatted; + if (!format_default_value(str, formatted)) { + return false; + } + + _default_value = formatted; + _has_default_value = true; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::set_default_value_literal +// Access: Public +// Description: Explicitly sets the default value to the given +// pre-formatted string. +//////////////////////////////////////////////////////////////////// +void DCAtomicField::ElementType:: +set_default_value_literal(const string &str) { + _default_value = str; + _has_default_value = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::add_default_value +// Access: Public +// Description: Appends the indicated value as the next array element +// value for the default value for this type. +// +// Returns true if the element type reasonably accepts a +// default value of numeric type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +add_default_value(double num) { + string formatted; + if (!format_default_value(num, formatted)) { + return false; + } + + _default_value += formatted; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::end_array +// Access: Public +// Description: Called by the parser after a number of calls to +// add_default_value(), to indicate the array has been +// completed. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +end_array() { + switch (_type) { + case ST_int16array: + case ST_uint16array: + case ST_int32array: + case ST_uint32array: + case ST_blob: + { + // We've accumulated all the elements of the array; now we must + // prepend the array length. + int length = _default_value.length(); + _default_value = + string(1, (char)(length & 0xff)) + + string(1, (char)((length >> 8) & 0xff)) + + _default_value; + } + return true; + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::format_default_value +// Access: Private +// Description: Formats the indicated default value to a sequence of +// bytes, according to the element type. Returns true +// if the element type reasonably accepts a number, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +format_default_value(double num, string &formatted) const { + double real_value = num * _divisor; + int int_value = (int)floor(real_value + 0.5); + + switch (_type) { + case ST_int8: + case ST_uint8: + case ST_blob: + formatted = string(1, (char)(int_value & 0xff)); + break; + + case ST_int16: + case ST_uint16: + case ST_int16array: + case ST_uint16array: + formatted = + string(1, (char)(int_value & 0xff)) + + string(1, (char)((int_value >> 8) & 0xff)); + break; + + case ST_int32: + case ST_uint32: + case ST_int32array: + case ST_uint32array: + formatted = + string(1, (char)(int_value & 0xff)) + + string(1, (char)((int_value >> 8) & 0xff)) + + string(1, (char)((int_value >> 16) & 0xff)) + + string(1, (char)((int_value >> 24) & 0xff)); + break; + + case ST_int64: + // We don't fully support default values for int64. The + // high-order 32 bits cannot be specified. + formatted = + string(1, (char)(int_value & 0xff)) + + string(1, (char)((int_value >> 8) & 0xff)) + + string(1, (char)((int_value >> 16) & 0xff)) + + string(1, (char)((int_value >> 24) & 0xff)) + + ((int_value & 0x80000000) != 0 ? string(4, '\xff') : string(4, '\0')); + break; + + case ST_uint64: + // We don't fully support default values for int64. The + // high-order 32 bits cannot be specified. + formatted = + string(1, (char)(int_value & 0xff)) + + string(1, (char)((int_value >> 8) & 0xff)) + + string(1, (char)((int_value >> 16) & 0xff)) + + string(1, (char)((int_value >> 24) & 0xff)) + + string(4, '\0'); + break; + + case ST_float64: + // This may not be fully portable. + formatted = string((char *)&real_value, 8); +#ifdef IS_BIG_ENDIAN + { + // Reverse the byte ordering for big-endian machines. + string str; + str = reserve(8); + + for (size_t i = 0; i < 8; i++) { + str += formatted[length - 1 - i]; + } + formatted = str; + } +#endif + break; + + case ST_string: + // It doesn't make sense to assign a numeric default value to a + // string. + return false; + + case ST_invalid: + break; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::ElementType::format_default_value +// Access: Private +// Description: Formats the indicated default value to a sequence of +// bytes, according to the element type. Returns true +// if the element type reasonably accepts a string, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField::ElementType:: +format_default_value(const string &str, string &formatted) const { + switch (_type) { + case ST_string: + case ST_blob: + { + int length = str.length(); + formatted = + string(1, (char)(length & 0xff)) + + string(1, (char)((length >> 8) & 0xff)) + + str; + } + break; + + default: + // It doesn't make sense to assign a string default to a number. + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: DCAtomicField::as_atomic_field // Access: Public, Virtual @@ -51,6 +324,20 @@ get_element_type(int n) const { return _elements[n]._type; } +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::get_element_name +// Access: Public +// Description: Returns the name of the nth element of the field. +// This name is strictly for documentary purposes; it +// does not generally affect operation. If a name is +// not specified, this will be the empty string. +//////////////////////////////////////////////////////////////////// +string DCAtomicField:: +get_element_name(int n) const { + nassertr(n >= 0 && n < (int)_elements.size(), string()); + return _elements[n]._name; +} + //////////////////////////////////////////////////////////////////// // Function: DCAtomicField::get_element_divisor // Access: Public @@ -66,6 +353,34 @@ get_element_divisor(int n) const { return _elements[n]._divisor; } +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::get_element_default +// Access: Public +// Description: Returns the pre-formatted default value associated +// with the nth element of the field. This is only +// valid if has_element_default() returns true, in which +// case this string represents the bytes that should be +// assigned to the field as a default value. +//////////////////////////////////////////////////////////////////// +string DCAtomicField:: +get_element_default(int n) const { + nassertr(has_element_default(n), string()); + nassertr(n >= 0 && n < (int)_elements.size(), string()); + return _elements[n]._default_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCAtomicField::has_element_default +// Access: Public +// Description: Returns true if the nth element of the field has a +// default value specified, false otherwise. +//////////////////////////////////////////////////////////////////// +bool DCAtomicField:: +has_element_default(int n) const { + nassertr(n >= 0 && n < (int)_elements.size(), false); + return _elements[n]._has_default_value; +} + //////////////////////////////////////////////////////////////////// // Function: DCAtomicField::is_required // Access: Public diff --git a/direct/src/dcparser/dcAtomicField.h b/direct/src/dcparser/dcAtomicField.h index cddcd9a999..4d79a7e257 100644 --- a/direct/src/dcparser/dcAtomicField.h +++ b/direct/src/dcparser/dcAtomicField.h @@ -25,7 +25,10 @@ PUBLISHED: int get_num_elements() const; DCSubatomicType get_element_type(int n) const; + string get_element_name(int n) const; int get_element_divisor(int n) const; + string get_element_default(int n) const; + bool has_element_default(int n) const; bool is_required() const; bool is_broadcast() const; @@ -46,8 +49,23 @@ public: // definition as read from the file. class ElementType { public: + ElementType(); + bool set_default_value(double num); + bool set_default_value(const string &str); + void set_default_value_literal(const string &str); + + bool add_default_value(double num); + bool end_array(); + DCSubatomicType _type; + string _name; int _divisor; + string _default_value; + bool _has_default_value; + + private: + bool format_default_value(double num, string &formatted) const; + bool format_default_value(const string &str, string &formatted) const; }; typedef vector Elements; diff --git a/direct/src/dcparser/dcLexer.lxx b/direct/src/dcparser/dcLexer.lxx index 459dd6236a..4049e63725 100644 --- a/direct/src/dcparser/dcLexer.lxx +++ b/direct/src/dcparser/dcLexer.lxx @@ -188,6 +188,66 @@ scan_quoted_string() { return result; } +// scan_hex_string reads a string of hexadecimal digits delimited by +// angle brackets and returns the representative string. +static string +scan_hex_string() { + string result; + + // We don't touch the current line number and column number during + // scanning, so that if we detect an error while scanning the string + // (e.g. an unterminated string), we'll report the error as + // 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 + // variables, we'll operate on our own local variables for the + // interim. + int line = line_number; + int col = col_number; + + bool odd = false; + int last = 0; + int c; + c = read_char(line, col); + while (c != '>' && c != EOF) { + int value; + if (c >= '0' && c <= '9') { + value = c - '0'; + } else if (c >= 'a' && c <= 'f') { + value = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + value = c - 'A' + 10; + } else { + line_number = line; + col_number = col; + dcyyerror("Invalid hex digit."); + return string(); + } + + odd = !odd; + if (odd) { + last = value; + } else { + result += (char)((last << 4) | value); + } + c = read_char(line, col); + } + + if (c == EOF) { + dcyyerror("This hex string is unterminated."); + return string(); + } else if (odd) { + dcyyerror("Odd number of hex digits."); + return string(); + } + + line_number = line; + col_number = col; + + return result; +} + // eat_c_comment scans past all characters up until the first */ // encountered. static void @@ -229,6 +289,7 @@ inline void accept() { %} INTEGERNUM ([+-]?([0-9]+)) +HEXNUM 0x[0-9a-fA-F]* REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) %% @@ -403,6 +464,14 @@ mol[0-9]+ { return INTEGER; } +{HEXNUM} { + // A hexadecimal integer number. + accept(); + dcyylval.u.integer = strtoul(yytext+2, NULL, 16); + dcyylval.str = yytext; + return INTEGER; +} + {REALNUM} { // A floating-point number. accept(); @@ -418,6 +487,13 @@ mol[0-9]+ { return STRING; } +[<] { + // Long hex string. + accept(); + dcyylval.str = scan_hex_string(); + return HEX_STRING; +} + [A-Za-z_][A-Za-z_0-9]* { // Identifier. accept(); diff --git a/direct/src/dcparser/dcParser.yxx b/direct/src/dcparser/dcParser.yxx index ee0b22dcbb..0d16a52334 100644 --- a/direct/src/dcparser/dcParser.yxx +++ b/direct/src/dcparser/dcParser.yxx @@ -23,6 +23,7 @@ static DCFile *dc_file = (DCFile *)NULL; static DCClass *current_class = (DCClass *)NULL; static DCAtomicField *current_atomic = (DCAtomicField *)NULL; static DCMolecularField *current_molecular = (DCMolecularField *)NULL; +static DCAtomicField::ElementType atomic_element; //////////////////////////////////////////////////////////////////// // Defining the interface to the parser. @@ -43,7 +44,7 @@ dc_cleanup_parser() { %token INTEGER %token REAL -%token STRING IDENTIFIER +%token STRING HEX_STRING IDENTIFIER %token KW_DCLASS @@ -171,27 +172,112 @@ parameter_list: ; nonempty_parameter_list: - subatomic_type - | nonempty_parameter_list ',' subatomic_type + atomic_element + | nonempty_parameter_list ',' atomic_element ; -subatomic_type: - type_token +atomic_element: + type_token { - DCAtomicField::ElementType et; - et._type = $1; - et._divisor = 1; - current_atomic->_elements.push_back(et); + atomic_element = DCAtomicField::ElementType(); + atomic_element._type = $1; } - | type_token '/' INTEGER + atomic_element_definition { - DCAtomicField::ElementType et; - et._type = $1; - et._divisor = $3; - current_atomic->_elements.push_back(et); + current_atomic->_elements.push_back(atomic_element) } ; +atomic_element_definition: + empty + | atomic_element_definition '/' INTEGER +{ + atomic_element._divisor = $3; +} + | atomic_element_definition IDENTIFIER +{ + atomic_element._name = $2; +} + | atomic_element_definition '=' INTEGER +{ + if (!atomic_element.set_default_value($3)) { + yyerror("Invalid default value: " + $3); + } +} + | atomic_element_definition '=' REAL +{ + if (!atomic_element.set_default_value($3)) { + yyerror("Invalid default value: " + $3); + } +} + | atomic_element_definition '=' STRING +{ + if (!atomic_element.set_default_value($3)) { + yyerror("Invalid default value: \"" + $3 + "\""); + } +} + | atomic_element_definition '=' HEX_STRING +{ + atomic_element.set_default_value($3); +} + | atomic_element_definition '=' '{' default_array '}' +{ + if (!atomic_element.end_array()) { + yyerror("Array default value inappropriate"); + } else { + atomic_element._has_default_value = true; + } +} + ; + +default_array: + empty + | default_array_def maybe_comma + ; + +maybe_comma: + empty + | ',' + ; + +default_array_def: + default_array_element + | default_array_def ',' default_array_element + ; + +default_array_element: + INTEGER +{ + if (!atomic_element.add_default_value($1)) { + yyerror("Invalid default value: " + $1); + } +} + | REAL +{ + if (!atomic_element.add_default_value($1)) { + yyerror("Invalid default value: " + $1); + } +} + | INTEGER '*' INTEGER +{ + for (int i = 0; i < $3; i++) { + if (!atomic_element.add_default_value($1)) { + yyerror("Invalid default value: " + $1); + break; + } + } +} + | REAL '*' INTEGER +{ + for (int i = 0; i < $3; i++) { + if (!atomic_element.add_default_value($1)) { + yyerror("Invalid default value: " + $1); + break; + } + } +} + ; + type_token: KW_INT8 { diff --git a/direct/src/dcparser/dcbase.h b/direct/src/dcparser/dcbase.h index de294e7313..66d21ee4d5 100644 --- a/direct/src/dcparser/dcbase.h +++ b/direct/src/dcparser/dcbase.h @@ -20,6 +20,7 @@ #include #include #include +#include #else @@ -75,6 +76,14 @@ using namespace std; // string instead. typedef string Filename; +// Panda defines IS_BIG_ENDIAN or IS_LITTLE_ENDIAN as appropriate. +// Outside of panda, you'll have to do it yourself. If neither is +// defined, we'll assume IS_LITTLE_ENDIAN. +#if !defined(IS_BIG_ENDIAN) && !defined(IS_LITTLE_ENDIAN) +#define IS_LITTLE_ENDIAN +#endif + + #endif // WITHIN_PANDA #endif // DCBASE_H diff --git a/direct/src/dcparser/hashGenerator.cxx b/direct/src/dcparser/hashGenerator.cxx index f85c9c24b7..d540f9777e 100644 --- a/direct/src/dcparser/hashGenerator.cxx +++ b/direct/src/dcparser/hashGenerator.cxx @@ -7,18 +7,15 @@ #include "primeNumberGenerator.h" // We multiply each consecutive integer by the next prime number and -// add it to the total, so in theory we will truly generate a unique -// hash number for each unique sequence of ints, as long as the number -// of ints does not exceed the number of prime numbers we have, and we -// do not overflow the limits of a 32-bit integer. +// add it to the total. This will generate pretty evenly-distributed +// hash numbers for an arbitrary sequence of ints. // We do recycle the prime number table at some point, just to keep it -// from growing insanely large, however, and we also truncate -// everything to the low-order 32 bits, so we introduce ambiguity in -// this way. +// from growing insanely large, however (and to avoid wasting time +// computing large prime numbers unnecessarily), and we also truncate +// the result to the low-order 32 bits. static const int max_prime_numbers = 10000; -static PrimeNumberGenerator primes; //////////////////////////////////////////////////////////////////// // Function: HashGenerator::Constructor @@ -39,7 +36,7 @@ HashGenerator() { void HashGenerator:: add_int(int num) { nassertv(_index >= 0 && _index < max_prime_numbers); - _hash += (int)primes[_index] * num; + _hash += _primes[_index] * num; _index = (_index + 1) % max_prime_numbers; } diff --git a/direct/src/dcparser/hashGenerator.h b/direct/src/dcparser/hashGenerator.h index 38977da6a4..ea5d98255b 100644 --- a/direct/src/dcparser/hashGenerator.h +++ b/direct/src/dcparser/hashGenerator.h @@ -7,6 +7,7 @@ #define DCHASHGENERATOR_H #include "dcbase.h" +#include "primeNumberGenerator.h" //////////////////////////////////////////////////////////////////// // Class : HashGenerator @@ -25,6 +26,7 @@ public: private: long _hash; int _index; + PrimeNumberGenerator _primes; }; #endif