Frobby  0.9.5
Scanner.cpp
Go to the documentation of this file.
1 /* Frobby: Software for monomial ideal computations.
2  Copyright (C) 2007 Bjarke Hammersholt Roune (www.broune.com)
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program. If not, see http://www.gnu.org/licenses/.
16 */
17 #include "stdinc.h"
18 #include "Scanner.h"
19 
20 #include "VarNames.h"
21 #include "IOHandler.h"
22 #include "error.h"
23 #include "FrobbyStringStream.h"
24 #include <limits>
25 
26 static const size_t BufferSize = 10024;
27 
28 Scanner::Scanner(const string& formatName, FILE* in):
29  _in(in),
30  _lineNumber(1),
31  _char(' '),
32  _tmpString(0),
33  _tmpStringCapacity(16),
34  _formatName(formatName),
35  _buffer(BufferSize),
36  _bufferPos(_buffer.end()) {
39  _tmpString = new char[16];
40 }
41 
42 auto_ptr<IOHandler> Scanner::createIOHandler() const {
44 }
45 
46 void Scanner::expect(const char* str) {
47  ASSERT(str != 0);
48 
49  eatWhite();
50 
51  const char* it = str;
52  while (*it != '\0') {
53  int character = getChar();
54  if (*it == character) {
55  ++it;
56  continue;
57  }
58 
59  // Read the rest of what is there to improve error message.
60  // TODO: read at least one char in total even if not alnum.
62  if (character == EOF && it == str)
63  got << "no more input";
64  else {
65  got << '\"' << string(str, it);
66  if (isalnum(character))
67  got << static_cast<char>(character);
68  while (isalnum(peek()))
69  got << static_cast<char>(getChar());
70  got << '\"';
71  }
72 
74  }
75 }
76 
78  // TODO: get this moved into the null format itself.
79  if (_formatName == "null")
80  return;
81 
82  eatWhite();
83  if (getChar() != EOF)
84  reportErrorUnexpectedToken("no more input", "");
85 }
86 
87 void Scanner::errorExpectTwo(char a, char b, int got) {
88  ASSERT(a != got && b != got);
90  err << a << " or " << b;
92 }
93 
94 void Scanner::errorExpectOne(char expected, int got) {
95  ASSERT(expected != got);
96  string expectedStr;
97  expectedStr += expected;
98  reportErrorUnexpectedToken(expectedStr, got);
99 }
100 
102  eatWhite();
103 
105 
106  if (peek() == '-' || peek() == '+')
107  _tmpString[0] = static_cast<char>(getChar());
108  else
109  _tmpString[0] = '+';
110 
111  size_t size = 1;
112 
113  while (isdigit(peek())) {
114  _tmpString[size] = static_cast<char>(getChar());
115  ++size;
116  if (size == _tmpStringCapacity)
117  growTmpString();
118  }
119  _tmpString[size] = '\0';
120 
121  if (size == 1)
122  reportErrorUnexpectedToken("an integer", "");
123 
124  return size;
125 }
126 
128  eatWhite();
129  if (peek() == '-' || peek() == '+')
130  reportErrorUnexpectedToken("integer without preceding sign", peek());
131  // todo: remove code duplication with readIntegerString.
132 
134 
135  size_t size = 0;
136  while (isdigit(peek())) {
137  _tmpString[size] = static_cast<char>(getChar());
138  ++size;
139  if (size == _tmpStringCapacity)
140  growTmpString();
141  }
142  _tmpString[size] = '\0';
143 
144  if (size == 0)
145  reportErrorUnexpectedToken("an integer", "");
146 
147  return size;
148 }
149 
150 void Scanner::parseInteger(mpz_class& integer, size_t size) {
151  // This code has a fast path for small integers and a slower path
152  // for longer integers. The largest number representable in 32 bits
153  // has 10 digits in base 10 and 1 char for the sign. If the number
154  // we are reading has less than 10 digits, then we calculate it
155  // directly without consulting GMP, which is faster.
156 
157  if (size < 10) {
158  signed long l = 0;
159  for (size_t i = 1; i < size; ++i)
160  l = 10 * l + (_tmpString[i] - '0');
161  if (_tmpString[0] == '-')
162  l = -l;
163  integer = l;
164  } else {
165  // For whatever reason mp_set_str does not support a + as the
166  // first character.
167  mpz_set_str(integer.get_mpz_t(), _tmpString + (_tmpString[0] != '-'), 10);
168  }
169 }
170 
171 void Scanner::readIntegerAndNegativeAsZero(mpz_class& integer) {
172  // Fast path for common case of reading a zero.
173  if (peek() == '0') {
174  getChar();
175  if (!isdigit(peek())) {
176  integer = 0;
177  return;
178  }
179  }
180 
181  size_t size = readIntegerString();
182  if (_tmpString[0] == '-')
183  integer = 0;
184  else
185  parseInteger(integer, size);
186 }
187 
188 void Scanner::readIntegerAndNegativeAsZero(string& integer) {
189  // Fast path for common case of reading a zero.
190  if (peek() == '0') {
191  getChar();
192  if (!isdigit(peek())) {
193  integer = '0';
194  return;
195  }
196  }
197 
199  if (_tmpString[0] == '-')
200  integer = '0';
201  else
202  integer = _tmpString + 1;
203 }
204 
205 void Scanner::readSizeT(size_t& size) {
207 
208  // Deal with different possibilities for how large size_t is.
209  if (sizeof(size_t) == sizeof(unsigned int)) {
210  if (!_integer.fits_uint_p()) {
211  FrobbyStringStream errorMsg;
212  errorMsg << "expected non-negative integer of size at most "
213  << numeric_limits<unsigned int>::max()
214  << " but got " << _integer << '.';
215  reportSyntaxError(*this, errorMsg);
216  }
217  size = (unsigned int)_integer.get_ui();
218  } else if (sizeof(size_t) == sizeof(unsigned long)) {
219  if (!_integer.fits_ulong_p()) {
220  FrobbyStringStream errorMsg;
221  errorMsg << "expected non-negative integer of size at most "
222  << numeric_limits<unsigned long>::max()
223  << " but got " << _integer << '.';
224  reportSyntaxError(*this, errorMsg);
225  }
226  size = _integer.get_ui(); // returns an unsigned long despite the name.
227  } else {
228  FrobbyStringStream errorMsg;
229  errorMsg <<
230  "Frobby does not work on this machine due to an "
231  "unexpected technical issue.\n"
232  "Please contact the developers of Frobby about this.\n"
233  "\n"
234  "Details that will be useful to the developers:\n"
235  " error location: Scanner::readSizeT\n"
236  " sizeof(size_t) = " << sizeof(size_t) << "\n"
237  " sizeof(unsigned int) = " << sizeof(unsigned int) << "\n"
238  " sizeof(unsigned long) = " << sizeof(unsigned long) << "\n";
239  reportInternalError(errorMsg);
240  }
241 }
242 
245  size_t newCapacity = _tmpStringCapacity * 2;
246  char* str = new char[newCapacity];
247  for (size_t i = 0; i < _tmpStringCapacity; ++i)
248  str[i] = _tmpString[i];
249  delete[] _tmpString;
250 
251  _tmpString = str;
252  _tmpStringCapacity = newCapacity;
253 }
254 
255 const char* Scanner::readIdentifier() {
256  eatWhite();
257  if (!isalpha(peek()))
259 
261 
262  size_t size = 0;
263  while (isalnum(peek()) || peek() == '_') {
264  _tmpString[size] = static_cast<char>(getChar());
265  ++size;
266  if (size == _tmpStringCapacity)
267  growTmpString();
268  }
269  _tmpString[size] = '\0';
270 
271  return _tmpString;
272 }
273 
275  reportErrorUnexpectedToken("an identifier", "");
276 }
277 
278 void Scanner::errorReadVariable(const char* name) {
279  FrobbyStringStream errorMsg;
280  errorMsg << "Unknown variable \"" << name << "\". Maybe you forgot a *.";
281  reportSyntaxError(*this, errorMsg);
282 }
283 
285 (const string& expected, int got) {
286  FrobbyStringStream gotDescription;
287  if (got == EOF)
288  gotDescription << "no more input";
289  else
290  gotDescription << '\"' << static_cast<char>(got)<< '\"';
291  reportErrorUnexpectedToken(expected, gotDescription);
292 }
293 
295 (const string& expected, const string& got) {
296  FrobbyStringStream errorMsg;
297  errorMsg << "Expected " << expected;
298  if (got != "")
299  errorMsg << ", but got " << got;
300  errorMsg << '.';
301  reportSyntaxError(*this, errorMsg);
302 }
303 
305  if (_buffer.size() < _buffer.capacity() && (feof(_in) || ferror(_in)))
306  return EOF;
307  _buffer.resize(_buffer.capacity());
308  size_t read = fread(&_buffer[0], 1, _buffer.capacity(), _in);
309  _buffer.resize(read);
310  _bufferPos = _buffer.begin();
311  if (read == 0)
312  return EOF;
313  char c = *_bufferPos;
314  ++_bufferPos;
315  return c;
316 }
string getFormatNameIndicatingToGuessTheInputFormat()
Using the returned string in place of an (input) format name indicates to guess the format based on w...
Definition: IOHandler.cpp:234
auto_ptr< IOHandler > createIOHandler(const string &prefix)
Returns an IOHandler for the format whose name has the given prefix.
Definition: IOHandler.cpp:145
string autoDetectFormat(Scanner &in)
Return the format of what in is reading based on the first non-whitespace character.
Definition: IOHandler.cpp:198
static const size_t BufferSize
Definition: Scanner.cpp:26
A replacement for stringstream.
const string & getFormat() const
Definition: Scanner.h:61
auto_ptr< IOHandler > createIOHandler() const
Definition: Scanner.cpp:42
mpz_class _integer
Definition: Scanner.h:175
int peek()
Returns the next character or EOF.
Definition: Scanner.h:148
size_t readIntegerString()
Returns the size of the string.
Definition: Scanner.cpp:101
void growTmpString()
Definition: Scanner.cpp:243
char * _tmpString
Definition: Scanner.h:180
size_t _tmpStringCapacity
Definition: Scanner.h:181
void parseInteger(mpz_class &integer, size_t size)
Definition: Scanner.cpp:150
string _formatName
Definition: Scanner.h:183
Scanner(const string &formatName, FILE *in)
Construct a Scanner object.
Definition: Scanner.cpp:28
void reportErrorUnexpectedToken(const string &expected, int got)
Definition: Scanner.cpp:285
FILE * _in
Definition: Scanner.h:176
void eatWhite()
Reads past any whitespace, where whitespace is defined by the standard function isspace().
Definition: Scanner.h:267
vector< char >::iterator _bufferPos
Definition: Scanner.h:186
vector< char > _buffer
Definition: Scanner.h:185
void errorReadIdentifier()
Definition: Scanner.cpp:274
void expectEOF()
Require that there is no more input.
Definition: Scanner.cpp:77
void readIntegerAndNegativeAsZero(mpz_class &integer)
Read an integer and set it to zero if it is negative.
Definition: Scanner.cpp:171
void expect(char expected)
Require the next character to be equal to expected.
Definition: Scanner.h:231
const char * readIdentifier()
The returned string is only valid until the next method on this object gets called.
Definition: Scanner.cpp:255
void errorExpectTwo(char a, char b, int got)
Definition: Scanner.cpp:87
void readSizeT(size_t &size)
Reads a size_t, where the representable range of that type determines when the number is too big.
Definition: Scanner.cpp:205
int readBuffer()
Definition: Scanner.cpp:304
void readInteger(mpz_class &integer)
Read an arbitrary-precision integer.
Definition: Scanner.h:238
void errorExpectOne(char expected, int got)
Definition: Scanner.cpp:94
void setFormat(const string &format)
Definition: Scanner.h:62
void errorReadVariable(const char *name)
Definition: Scanner.cpp:278
int getChar()
Definition: Scanner.h:272
size_t readIntegerStringNoSign()
Returns the size of the string.
Definition: Scanner.cpp:127
void reportInternalError(const string &errorMsg)
Definition: error.cpp:29
void reportSyntaxError(const Scanner &scanner, const string &errorMsg)
Definition: error.cpp:44
#define ASSERT(X)
Definition: stdinc.h:86