Panda3D
rangeDescription.cxx
1 // Filename: rangeDescription.cxx
2 // Created by: drose (07Sep03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "rangeDescription.h"
16 #include "string_utils.h"
17 #include "pnotify.h"
18 
19 ////////////////////////////////////////////////////////////////////
20 // Function: RangeDescription::Constructor
21 // Access: Public
22 // Description:
23 ////////////////////////////////////////////////////////////////////
24 RangeDescription::
25 RangeDescription() {
26 }
27 
28 ////////////////////////////////////////////////////////////////////
29 // Function: RangeDescription::parse_parameter
30 // Access: Public
31 // Description: Parses a string of comma- and hyphen-delimited
32 // unicode values, in decimal and/or hex, including
33 // possible bracket-delimited ASCII characters, as may
34 // have been passed on a command line. Returns true if
35 // the parameter is parsed correctly, false otherwise.
36 ////////////////////////////////////////////////////////////////////
38 parse_parameter(const string &param) {
39  // First, go through and separate the string by commas. We have to
40  // do this by hand instead of calling tokenize(), because we also
41  // have to scan for square brackets, which may contain nested
42  // commas.
43  size_t p = 0;
44  while (p < param.length()) {
45  size_t q = param.find_first_of("[,", p);
46  if (q == string::npos) {
47  return parse_word(trim(param.substr(p)));
48  }
49  if (!parse_word(trim(param.substr(p, q - p)))) {
50  return false;
51  }
52 
53  if (param[q] == '[') {
54  // A square bracket means we must search for the matching square
55  // bracket. However, a right bracket immediately after the left
56  // bracket doesn't count; we start the scan after that.
57  p = param.find("]", q + 2);
58  if ( p == string::npos) {
59  nout << "Unclosed open bracket.\n";
60  return false;
61  }
62  if (!parse_bracket(param.substr(q + 1, p - q - 1))) {
63  return false;
64  }
65  p = p + 1;
66 
67  } else {
68  // Otherwise, if the separator was just a comma, the next
69  // character begins the next word.
70  p = q + 1;
71  }
72  }
73 
74  return true;
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: RangeDescription::output
79 // Access: Public
80 // Description:
81 ////////////////////////////////////////////////////////////////////
82 void RangeDescription::
83 output(ostream &out) const {
84  bool first_time = true;
85  RangeList::const_iterator ri;
86  for (ri = _range_list.begin(); ri != _range_list.end(); ++ri) {
87  const Range &range = (*ri);
88  if (!first_time) {
89  out << ",";
90  }
91  first_time = false;
92  if (range._from_code == range._to_code) {
93  out << range._from_code;
94  } else {
95  out << range._from_code << "-" << range._to_code;
96  }
97  }
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: RangeDescription::parse_word
102 // Access: Private
103 // Description: Parses a single "word", i.e. the text delimited by
104 // commas, that might be listed on the command line.
105 // This is generally either the empty string, a single
106 // number, or a pair of numbers separated by a hyphen.
107 ////////////////////////////////////////////////////////////////////
108 bool RangeDescription::
109 parse_word(const string &word) {
110  if (word.empty()) {
111  return true;
112  }
113 
114  // It's not empty, so see if it includes a hyphen.
115  size_t hyphen = word.find('-');
116  if (hyphen == string::npos) {
117  // Nope, just one number.
118  int code;
119  if (!parse_code(word, code)) {
120  return false;
121  }
122  add_singleton(code);
123 
124  } else {
125  // Two numbers separated by a hyphen.
126  int from_code, to_code;
127  if (!parse_code(word.substr(0, hyphen), from_code)) {
128  return false;
129  }
130  if (!parse_code(word.substr(hyphen + 1), to_code)) {
131  return false;
132  }
133  add_range(from_code, to_code);
134  }
135 
136  return true;
137 }
138 
139 ////////////////////////////////////////////////////////////////////
140 // Function: RangeDescription::parse_code
141 // Access: Private
142 // Description: Parses a single numeric value, either decimal or
143 // hexadecimal, and stores it in the indicated
144 // parameter. Returns true if successful, false
145 // otherwise.
146 ////////////////////////////////////////////////////////////////////
147 bool RangeDescription::
148 parse_code(const string &word, int &code) {
149  string str = trim(word);
150  const char *nptr = str.c_str();
151  char *endptr;
152  code = strtol(nptr, &endptr, 0);
153  if (*endptr == '\0') {
154  return true;
155  }
156 
157  nout << "Invalid Unicode value: " << word << "\n";
158  return false;
159 }
160 
161 ////////////////////////////////////////////////////////////////////
162 // Function: RangeDescription::parse_bracket
163 // Access: Private
164 // Description: Parses the text listed between square brackets on the
165 // command line.
166 ////////////////////////////////////////////////////////////////////
167 bool RangeDescription::
168 parse_bracket(const string &str) {
169  string::const_iterator si;
170  si = str.begin();
171  while (si != str.end()) {
172  int ch = (*si);
173  ++si;
174  if (si != str.end() && (*si) == '-') {
175  // A hyphen indicates a range.
176  ++si;
177  if (si == str.end()) {
178  // Unless the hyphen is the last character.
179  add_singleton(ch);
180  add_singleton('-');
181  } else {
182  add_range(ch, (*si));
183  ++si;
184  }
185  } else {
186  // Anything other than a hyphen indicates a singleton.
187  add_singleton(ch);
188  }
189  }
190 
191  return true;
192 }
bool parse_parameter(const string &param)
Parses a string of comma- and hyphen-delimited unicode values, in decimal and/or hex, including possible bracket-delimited ASCII characters, as may have been passed on a command line.