public bool NextToken() {
int ch = 0;
do {
ch = file.Read();
} while (ch != -1 && IsWhitespace(ch));
if (ch == -1)
return false;
// Note: We have to initialize stringValue here, after we've looked for the end of the stream,
// to ensure that we don't lose the value of a token that might end exactly at the end
// of the stream
StringBuilder outBuf = null;
stringValue = EMPTY;
switch (ch) {
case '[':
type = TK_START_ARRAY;
break;
case ']':
type = TK_END_ARRAY;
break;
case '/': {
outBuf = new StringBuilder();
type = TK_NAME;
while (true) {
ch = file.Read();
if (ch == -1 || IsDelimiter(ch) || IsWhitespace(ch))
break;
if (ch == '#') {
ch = (GetHex(file.Read()) << 4) + GetHex(file.Read());
}
outBuf.Append((char)ch);
}
BackOnePosition(ch);
break;
}
case '>':
ch = file.Read();
if (ch != '>')
ThrowError("'>' not expected");
type = TK_END_DIC;
break;
case '<': {
int v1 = file.Read();
if (v1 == '<') {
type = TK_START_DIC;
break;
}
outBuf = new StringBuilder();
type = TK_STRING;
hexString = true;
int v2 = 0;
while (true) {
while (IsWhitespace(v1))
v1 = file.Read();
if (v1 == '>')
break;
v1 = GetHex(v1);
if (v1 < 0)
break;
v2 = file.Read();
while (IsWhitespace(v2))
v2 = file.Read();
if (v2 == '>') {
ch = v1 << 4;
outBuf.Append((char)ch);
break;
}
v2 = GetHex(v2);
if (v2 < 0)
break;
ch = (v1 << 4) + v2;
outBuf.Append((char)ch);
v1 = file.Read();
}
if (v1 < 0 || v2 < 0)
ThrowError("Error reading string");
break;
}
case '%':
type = TK_COMMENT;
do {
ch = file.Read();
} while (ch != -1 && ch != '\r' && ch != '\n');
break;
case '(': {
outBuf = new StringBuilder();
type = TK_STRING;
hexString = false;
int nesting = 0;
while (true) {
ch = file.Read();
if (ch == -1)
break;
if (ch == '(') {
++nesting;
}
else if (ch == ')') {
--nesting;
}
else if (ch == '\\') {
bool lineBreak = false;
ch = file.Read();
switch (ch) {
case 'n':
ch = '\n';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case '(':
case ')':
case '\\':
break;
case '\r':
lineBreak = true;
ch = file.Read();
if (ch != '\n')
BackOnePosition(ch);
break;
case '\n':
lineBreak = true;
break;
default: {
if (ch < '0' || ch > '7') {
break;
}
int octal = ch - '0';
ch = file.Read();
if (ch < '0' || ch > '7') {
BackOnePosition(ch);
ch = octal;
break;
}
octal = (octal << 3) + ch - '0';
ch = file.Read();
if (ch < '0' || ch > '7') {
BackOnePosition(ch);
ch = octal;
break;
}
octal = (octal << 3) + ch - '0';
ch = octal & 0xff;
break;
}
}
if (lineBreak)
continue;
if (ch < 0)
break;
}
else if (ch == '\r') {
ch = file.Read();
if (ch < 0)
break;
if (ch != '\n') {
BackOnePosition(ch);
ch = '\n';
}
}
if (nesting == -1)
break;
outBuf.Append((char)ch);
}
if (ch == -1)
ThrowError("Error reading string");
break;
}
default: {
outBuf = new StringBuilder();
if (ch == '-' || ch == '+' || ch == '.' || (ch >= '0' && ch <= '9')) {
type = TK_NUMBER;
do {
outBuf.Append((char)ch);
ch = file.Read();
} while (ch != -1 && ((ch >= '0' && ch <= '9') || ch == '.'));
}
else {
type = TK_OTHER;
do {
outBuf.Append((char)ch);
ch = file.Read();
} while (ch != -1 && !IsDelimiter(ch) && !IsWhitespace(ch));
}
BackOnePosition(ch);
break;
}
}
if (outBuf != null)
stringValue = outBuf.ToString();
return true;
}