Finished scanner:

+ Octals are now handled properly in both quoted and
unquoted strings
+ Added support for \xXX style hex escapes in both
quoted and unquoted strings
+ Finished up the tests, so I think pretty much everything
should be working
NOTE: signed floating point numbers are no longer supported,
as they have no usefulness to sniffer rules
NOTE: extended format floating point numbers (i.e. 2.4e23) are
no longer supported, as they have no usefulness to sniffer rules.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@489 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder
2002-07-28 03:21:21 +00:00
parent 88c56012c8
commit dc685e9ad8
2 changed files with 659 additions and 164 deletions
+191 -130
View File
@@ -48,25 +48,26 @@ bool isHexChar(char ch);
bool isWhiteSpace(char ch); bool isWhiteSpace(char ch);
bool isOctalChar(char ch); bool isOctalChar(char ch);
bool isDecimalChar(char ch); bool isDecimalChar(char ch);
bool isPunctuation(char ch);
status_t status_t
Sniffer::parse(const char *rule, Rule *result, BString *parseError = NULL) { Sniffer::parse(const char *rule, Rule *result, BString *parseError = NULL) {
try { try {
if (!rule) if (!rule)
throw Err("Sniffer pattern error: NULL pattern"); throw new Err("Sniffer pattern error: NULL pattern");
if (!result) if (!result)
return B_BAD_VALUE; return B_BAD_VALUE;
if (stream.SetTo(rule) != B_OK) if (stream.SetTo(rule) != B_OK)
throw Err("Sniffer parser error: Unable to intialize token stream"); throw new Err("Sniffer parser error: Unable to intialize token stream");
double priority; double priority;
BList* exprList; BList* exprList;
// priority = parsePriority(); // priority = parsePriority();
} catch (Err &err) { } catch (Err *err) {
if (parseError) if (parseError && err)
parseError->SetTo((err.Msg() ? err.Msg() : "Sniffer parser rule: Unexpected error with no supplied error message")); parseError->SetTo((err->Msg() ? err->Msg() : "Sniffer parser rule: Unexpected error with no supplied error message"));
return B_BAD_MIME_SNIFFER_RULE; return B_BAD_MIME_SNIFFER_RULE;
} }
} }
@@ -102,7 +103,10 @@ Err::Msg() const {
void void
Err::SetMsg(const char *msg) { Err::SetMsg(const char *msg) {
delete fMsg; if (fMsg) {
delete fMsg;
fMsg = NULL;
}
if (msg == NULL) if (msg == NULL)
fMsg = NULL; fMsg = NULL;
else { else {
@@ -167,7 +171,7 @@ CharStream::IsEmpty() const {
char char
CharStream::Get() { CharStream::Get() {
if (fCStatus != B_OK) if (fCStatus != B_OK)
throw Err("Sniffer parser error: CharStream::Get() called on uninitialized CharStream object"); throw new Err("Sniffer parser error: CharStream::Get() called on uninitialized CharStream object");
if (fPos < fLen) if (fPos < fLen)
return fString[fPos++]; return fString[fPos++];
else { else {
@@ -179,11 +183,11 @@ CharStream::Get() {
void void
CharStream::Unget() { CharStream::Unget() {
if (fCStatus != B_OK) if (fCStatus != B_OK)
throw Err("Sniffer parser error: CharStream::Unget() called on uninitialized CharStream object"); throw new Err("Sniffer parser error: CharStream::Unget() called on uninitialized CharStream object");
if (fPos > 0) if (fPos > 0)
fPos--; fPos--;
else else
throw Err("Sniffer parser error: CharStream::Unget() called at beginning of character stream"); throw new Err("Sniffer parser error: CharStream::Unget() called at beginning of character stream");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -207,17 +211,17 @@ Token::Type() const {
const char* const char*
Token::String() const { Token::String() const {
throw Err("Sniffer scanner error: Token::String() called on non-string token"); throw new Err("Sniffer scanner error: Token::String() called on non-string token");
} }
int32 int32
Token::Int() const { Token::Int() const {
throw Err("Sniffer scanner error: Token::Int() called on non-integer token"); throw new Err("Sniffer scanner error: Token::Int() called on non-integer token");
} }
double double
Token::Float() const { Token::Float() const {
throw Err("Sniffer scanner error: Token::Float() called on non-float token"); throw new Err("Sniffer scanner error: Token::Float() called on non-float token");
} }
bool bool
@@ -226,7 +230,30 @@ Token::operator==(Token &ref) {
if (Type() == ref.Type()) { if (Type() == ref.Type()) {
switch (Type()) { switch (Type()) {
case CharacterString: case CharacterString:
return strcmp(String(), ref.String()) == 0; // printf(" str1 == '%s'\n", String());
// printf(" str2 == '%s'\n", ref.String());
// printf(" strcmp() == %d\n", strcmp(String(), ref.String()));
{
// strcmp() seems to choke on certain, non-normal ASCII chars
// (i.e. chars outside the usual alphabets, but still valid
// as far as ASCII is concerned), so we'll just compare the
// strings by hand to be safe.
const char *str1 = String();
const char *str2 = ref.String();
int len1 = strlen(str1);
int len2 = strlen(str2);
// printf("len1 == %d\n", len1);
// printf("len2 == %d\n", len2);
if (len1 == len2) {
for (int i = 0; i < len1; i++) {
// printf("i == %d, str1[%d] == %x, str2[%d] == %x\n", i, i, str1[i], i, str2[i]);
if (str1[i] != str2[i])
return false;
}
}
return true;
}
// return strcmp(String(), ref.String()) == 0;
case Integer: case Integer:
return Int() == ref.Int(); return Int() == ref.Int();
@@ -320,31 +347,34 @@ TokenStream::SetTo(const char *string) {
if (string) { if (string) {
CharStream stream(string); CharStream stream(string);
if (stream.InitCheck() != B_OK) if (stream.InitCheck() != B_OK)
throw Err("Sniffer scanner error: Unable to intialize character stream"); throw new Err("Sniffer scanner error: Unable to intialize character stream");
typedef enum TokenStreamScannerState { typedef enum TokenStreamScannerState {
tsssStart, tsssStart,
tsssOneSingle, tsssOneSingle,
tsssSingleEscape,
tsssOneDouble, tsssOneDouble,
tsssDoubleEscape,
tsssOneZero, tsssOneZero,
tsssZeroX, tsssZeroX,
tsssOneHex, tsssOneHex,
tsssTwoHex, tsssTwoHex,
tsssHexStringEnd,
tsssIntOrFloat, tsssIntOrFloat,
tsssFloat, tsssFloat,
tsssLonelyDecimalPoint, tsssLonelyDecimalPoint,
tsssLonelyMinusSign, tsssLonelyMinusOrPlusSign,
tsssNegativeInt, tsssPosNegInt,
tsssOneEscape,
tsssOneOctal,
tsssTwoOctal,
tsssUnquoted, tsssUnquoted,
tsssUnquotedEscape, tsssEscape,
tsssEscapeX,
tsssEscapeOneOctal,
tsssEscapeTwoOctal,
tsssEscapeOneHex,
tsssEscapeTwoHex
}; };
TokenStreamScannerState state = tsssStart; TokenStreamScannerState state = tsssStart;
TokenStreamScannerState escapedState;
// Used to remember which state to return to from an escape sequence
std::string charStr; // Used to build up character strings std::string charStr; // Used to build up character strings
char lastChar; // For two char lookahead char lastChar; // For two char lookahead
@@ -359,7 +389,7 @@ TokenStream::SetTo(const char *string) {
if (stream.IsEmpty()) if (stream.IsEmpty())
keepLooping = false; keepLooping = false;
else else
throw Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
break; break;
case '\t': case '\t':
@@ -378,9 +408,10 @@ TokenStream::SetTo(const char *string) {
state = tsssOneSingle; state = tsssOneSingle;
break; break;
case '+':
case '-': case '-':
charStr = ch; charStr = ch;
state = tsssLonelyMinusSign; state = tsssLonelyMinusOrPlusSign;
break; break;
case '.': case '.':
@@ -413,21 +444,24 @@ TokenStream::SetTo(const char *string) {
case '[': AddToken(LeftBracket); break; case '[': AddToken(LeftBracket); break;
case '\\': case '\\':
state = tsssOneEscape; charStr = ""; // Clear our string
state = tsssEscape;
escapedState = tsssUnquoted; // Unquoted strings begin with an escaped character
break; break;
case ']': AddToken(RightBracket); break; case ']': AddToken(RightBracket); break;
case '|': AddToken(Divider); break; case '|': AddToken(Divider); break;
default: default:
throw Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
} }
break; break;
case tsssOneSingle: case tsssOneSingle:
switch (ch) { switch (ch) {
case '\\': case '\\':
state = tsssSingleEscape; escapedState = state; // Save our state
state = tsssEscape; // Handle the escape sequence
break; break;
case '\'': case '\'':
AddString(charStr.c_str()); AddString(charStr.c_str());
@@ -435,7 +469,7 @@ TokenStream::SetTo(const char *string) {
break; break;
case 0x3: case 0x3:
if (stream.IsEmpty()) if (stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: unterminated single-quoted string")); throw new Err(std::string("Sniffer scanner error: unterminated single-quoted string"));
else else
charStr += ch; charStr += ch;
break; break;
@@ -445,20 +479,11 @@ TokenStream::SetTo(const char *string) {
} }
break; break;
case tsssSingleEscape:
// Check for a true end-of-text marker
if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: unterminated escape sequence in single-quoted string"));
else {
charStr += escapeChar(ch);
state = tsssOneSingle;
}
break;
case tsssOneDouble: case tsssOneDouble:
switch (ch) { switch (ch) {
case '\\': case '\\':
state = tsssDoubleEscape; escapedState = state; // Save our state
state = tsssEscape; // Handle the escape sequence
break; break;
case '"': case '"':
AddString(charStr.c_str()); AddString(charStr.c_str());
@@ -466,7 +491,7 @@ TokenStream::SetTo(const char *string) {
break; break;
case 0x3: case 0x3:
if (stream.IsEmpty()) if (stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: unterminated single-quoted string")); throw new Err(std::string("Sniffer scanner error: unterminated single-quoted string"));
else else
charStr += ch; charStr += ch;
break; break;
@@ -476,16 +501,6 @@ TokenStream::SetTo(const char *string) {
} }
break; break;
case tsssDoubleEscape:
// Check for a true end-of-text marker
if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: unterminated escape sequence in single-quoted string"));
else {
charStr += escapeChar(ch);
state = tsssOneDouble;
}
break;
case tsssOneZero: case tsssOneZero:
if (ch == 'x') { if (ch == 'x') {
charStr = ""; // Reinit, since we actually have a hex string charStr = ""; // Reinit, since we actually have a hex string
@@ -515,9 +530,9 @@ TokenStream::SetTo(const char *string) {
lastChar = ch; lastChar = ch;
state = tsssOneHex; state = tsssOneHex;
} else if (ch == 0x3 && stream.IsEmpty()) } else if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: incomplete hex code")); throw new Err(std::string("Sniffer scanner error: incomplete hex code"));
else else
throw Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
break; break;
case tsssOneHex: case tsssOneHex:
@@ -534,8 +549,9 @@ TokenStream::SetTo(const char *string) {
if (isHexChar(ch)) { if (isHexChar(ch)) {
lastChar = ch; lastChar = ch;
state = tsssOneHex; state = tsssOneHex;
} else if (isWhiteSpace(ch)) { } else if (isWhiteSpace(ch) || isPunctuation(ch)) {
AddString(charStr.c_str()); AddString(charStr.c_str());
stream.Unget(); // So punctuation gets handled properly
state = tsssStart; state = tsssStart;
} else if (ch == 0x3 && stream.IsEmpty()) { } else if (ch == 0x3 && stream.IsEmpty()) {
AddString(charStr.c_str()); AddString(charStr.c_str());
@@ -578,26 +594,26 @@ TokenStream::SetTo(const char *string) {
charStr += ch; charStr += ch;
state = tsssFloat; state = tsssFloat;
} else if (ch == 0x3 && stream.IsEmpty()) } else if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: incomplete floating point number")); throw new Err(std::string("Sniffer scanner error: incomplete floating point number"));
else else
throw Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
break; break;
case tsssLonelyMinusSign: case tsssLonelyMinusOrPlusSign:
if (isDecimalChar(ch)) { if (isDecimalChar(ch)) {
charStr += ch; charStr += ch;
state = tsssNegativeInt; state = tsssPosNegInt;
} else if (ch == 0x3 && stream.IsEmpty()) } else if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: incomplete negative integer")); throw new Err(std::string("Sniffer scanner error: incomplete signed integer"));
else else
throw Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
break; break;
case tsssNegativeInt: case tsssPosNegInt:
if (isDecimalChar(ch)) if (isDecimalChar(ch))
charStr += ch; charStr += ch;
else if (ch == '.') else if (ch == '.')
throw Err(std::string("Sniffer scanner error: negative floating point numbers are useless and thus illegal")); throw new Err(std::string("Sniffer scanner error: negative floating point numbers are useless and thus signs (both + and -) are disallowed on floating points"));
else { else {
// Terminate the number // Terminate the number
AddInt(charStr.c_str()); AddInt(charStr.c_str());
@@ -608,60 +624,16 @@ TokenStream::SetTo(const char *string) {
} }
break; break;
case tsssOneEscape:
if (isOctalChar(ch)) {
lastChar = ch;
state = tsssOneOctal;
} else {
// Check for a true end-of-text marker
if (ch == 0x3 && stream.IsEmpty())
throw Err(std::string("Sniffer scanner error: unterminated escape sequence"));
else {
charStr += escapeChar(ch);
state = tsssUnquoted;
}
}
break;
case tsssOneOctal:
if (isOctalChar(ch)) {
lastLastChar = lastChar;
lastChar = ch;
state = tsssTwoOctal;
} else {
// First handle the octal
charStr += octalToChar(lastChar);
// Push the new char back on and let the tsssUnquoted
// state handle it.
stream.Unget();
state = tsssUnquoted;
}
break;
case tsssTwoOctal:
if (isOctalChar(ch)) {
charStr += octalToChar(lastLastChar, lastChar, ch);
state = tsssUnquoted;
} else {
// First handle the octal
charStr += octalToChar(lastLastChar, lastChar);
// Push the new char back on and let the tsssUnquoted
// state handle it.
stream.Unget();
state = tsssUnquoted;
}
break;
case tsssUnquoted: case tsssUnquoted:
if (ch == '\\') if (ch == '\\') {
state = tsssUnquotedEscape; escapedState = state; // Save our state
else if (isWhiteSpace(ch)) { state = tsssEscape; // Handle the escape sequence
} else if (isWhiteSpace(ch) || isPunctuation(ch)) {
AddString(charStr.c_str()); AddString(charStr.c_str());
stream.Unget(); // In case it's punctuation, let tsssStart handle it
state = tsssStart; state = tsssStart;
} else if (ch == '\'' || ch == '"' || ch == '&') { } else if (ch == '\'' || ch == '"') {
throw Err(std::string("Sniffer scanner error: illegal unquoted character '") + ch + "'"); throw new Err(std::string("Sniffer scanner error: illegal unquoted character '") + ch + "'");
} else if (ch == 0x3 && stream.IsEmpty()) { } else if (ch == 0x3 && stream.IsEmpty()) {
AddString(charStr.c_str()); AddString(charStr.c_str());
keepLooping = false; keepLooping = false;
@@ -670,23 +642,78 @@ TokenStream::SetTo(const char *string) {
} }
break; break;
case tsssUnquotedEscape: case tsssEscape:
// Check for a true end-of-text marker if (isOctalChar(ch)) {
if (ch == 0x3 && stream.IsEmpty()) lastChar = ch;
throw Err(std::string("Sniffer scanner error: unterminated escape sequence in unquoted string")); state = tsssEscapeOneOctal;
else { } else {
charStr += escapeChar(ch); // Check for a true end-of-text marker
state = tsssUnquoted; if (ch == 0x3 && stream.IsEmpty())
} throw new Err(std::string("Sniffer scanner error: unterminated escape sequence"));
else {
charStr += escapeChar(ch);
state = escapedState; // Return to the state we were in before the escape
}
}
break; break;
case tsssEscapeX:
if (isHexChar(ch)) {
lastChar = ch;
state = tsssEscapeOneHex;
} else
throw new Err(std::string("Sniffer scanner error: incomplete hex code"));
break;
case tsssEscapeOneOctal:
if (isOctalChar(ch)) {
lastLastChar = lastChar;
lastChar = ch;
state = tsssEscapeTwoOctal;
} else {
// First handle the octal
charStr += octalToChar(lastChar);
// Push the new char back on and let the state we
// were in when the escape sequence was hit handle it.
stream.Unget();
state = escapedState;
}
break;
case tsssEscapeTwoOctal:
if (isOctalChar(ch)) {
charStr += octalToChar(lastLastChar, lastChar, ch);
state = escapedState;
} else {
// First handle the octal
charStr += octalToChar(lastLastChar, lastChar);
// Push the new char back on and let the state we
// were in when the escape sequence was hit handle it.
stream.Unget();
state = escapedState;
}
break;
case tsssEscapeOneHex:
if (isHexChar(ch)) {
charStr += hexToChar(lastChar, ch);
state = escapedState;
} else if (ch == 0x3 && stream.IsEmpty())
throw new Err(std::string("Sniffer scanner error: incomplete escaped hex code (the number of hex digits must be a multiple of two)"));
else
throw new Err(std::string("Sniffer scanner error: unexpected character '") + ch + "'");
break;
} }
} }
if (state == tsssStart) if (state == tsssStart)
fCStatus = B_OK; fCStatus = B_OK;
else else
throw Err("Sniffer pattern error: unterminated rule"); throw new Err("Sniffer pattern error: unterminated rule");
} }
return fCStatus; return fCStatus;
} }
@@ -751,11 +778,29 @@ TokenStream::AddFloat(const char *str) {
char char
escapeChar(char ch) { escapeChar(char ch) {
// Is there an easier way to do this? I'm not sure. :-) // I've manually handled all the escape sequences I could come
std::string format = std::string("\\") + ch; // up with, and for anything else I just return the character
char str[3]; // Two should be enough but I'm paranoid :-) // passed in. Hex escapes are handled elsewhere, so \x just
sprintf(str, format.c_str()); // returns 'x'. Similarly, octals are handled elsewhere, so \0
return str[0]; // through \9 just return '0' through '9'.
switch (ch) {
case 'a':
return '\a';
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
default:
return ch;
}
} }
// Converts 0x|hi|low| to a single char // Converts 0x|hi|low| to a single char
@@ -774,7 +819,7 @@ hexToChar(char hex) {
else if ('A' <= hex && hex <= 'F') else if ('A' <= hex && hex <= 'F')
return hex-'a'+10; return hex-'a'+10;
else else
throw Err(std::string("Sniffer parser error: invalid hex digit '") + hex + "' passed to hexToChar()"); throw new Err(std::string("Sniffer parser error: invalid hex digit '") + hex + "' passed to hexToChar()");
} }
char char
@@ -792,7 +837,7 @@ octalToChar(char hi, char mid, char low) {
if (isOctalChar(hi) && isOctalChar(mid) && isOctalChar(low)) { if (isOctalChar(hi) && isOctalChar(mid) && isOctalChar(low)) {
return ((hi-'0') << 6) | ((mid-'0') << 3) | (low-'0'); return ((hi-'0') << 6) | ((mid-'0') << 3) | (low-'0');
} else } else
throw Err(std::string("Sniffer parser error: invalid octal digit passed to hexToChar()")); throw new Err(std::string("Sniffer parser error: invalid octal digit passed to hexToChar()"));
} }
bool bool
@@ -817,6 +862,22 @@ isDecimalChar(char ch) {
return ('0' <= ch && ch <= '9'); return ('0' <= ch && ch <= '9');
} }
bool
isPunctuation(char ch) {
switch (ch) {
case '&':
case '(':
case ')':
case ':':
case '[':
case ']':
case '|':
return true;
default:
return false;
}
}
const char* const char*
Sniffer::tokenTypeToString(TokenType type) { Sniffer::tokenTypeToString(TokenType type) {
switch (type) { switch (type) {
+468 -34
View File
@@ -87,38 +87,458 @@ ParserTest::ScannerTest() {
S("EFGH"), S("EFGH"),
T(RightParen) T(RightParen)
} }
} /*, },
{ "0.5 \n [0:3] \t ('ABCD' \n | 'abcd' | 'EFGH')", NULL }, { "0.5 \n [0:3] \t ('ABCD' \n | 'abcd' | 'EFGH')", 13,
{ "0.8 [ 0 : 3 ] ('ABCDEFG' | 'abcdefghij')", NULL }, { F(0.5),
{ "0.8 [0:3] ('ABCDEFG' & 'abcdefg')", NULL }, T(LeftBracket),
{ "1.0 ('ABCD') | ('EFGH')", NULL }, I(0),
{ "1.0 [0:3] ('ABCD') | [2:4] ('EFGH')", NULL }, T(Colon),
{ "0.8 [0:3] (\\077Mkl0x34 & 'abcdefgh')", NULL }, I(3),
{ "0.8 [0:3] (\\077034 & 'abcd')", NULL }, T(RightBracket),
{ "0.8 [0:3] (\\077\\034 & 'ab')", NULL }, T(LeftParen),
{ "0.8 [0:3] (\\77\\034 & 'ab')", NULL }, S("ABCD"),
{ "0.8 [0:3] (\\7 & 'a')", NULL }, T(Divider),
{ "0.8 [0:3] (\"\\17\" & 'a')", NULL }, S("abcd"),
{ "0.8 [0:3] ('\\17' & 'a')", NULL }, T(Divider),
{ "0.8 [0:3] (\\g & 'a')", NULL }, S("EFGH"),
{ "0.8 [0:3] (\\g&\\b)", NULL }, T(RightParen)
{ "0.8 [0:3] (\\g\\&b & 'abc')", NULL }, }
{ "0.8 [0:3] (0x3457 & 'ab')", NULL }, },
{ "0.8 [0:3] (0xA4b7 & 'ab')", NULL }, { "0.8 [ 0 : 3 ] ('ABCDEFG' | 'abcdefghij')", 11,
{ "0.8 [0:3] ('ab\"' & 'abc')", NULL }, { F(0.8),
{ "0.8 [0:3] (\"ab\\\"\" & 'abc')", NULL }, T(LeftBracket),
{ "0.8 [0:3] (\"ab\\A\" & 'abc')", NULL }, I(0),
{ "0.8 [0:3] (\"ab'\" & 'abc')", NULL }, T(Colon),
{ "0.8 [0:3] (\"ab\\\\\" & 'abc')", NULL }, I(3),
{ "0.8 [-5:-3] (\"abc\" & 'abc')", NULL }, T(RightBracket),
{ "0.8 [5:3] (\"abc\" & 'abc')", NULL }, T(LeftParen),
{ "1.2 ('ABCD')", NULL }, S("ABCDEFG"),
{ ".2 ('ABCD')", NULL }, T(Divider),
{ "0. ('ABCD')", NULL }, S("abcdefghij"),
{ "-1 ('ABCD')", NULL }, T(RightParen)
{ "+1 ('ABCD')", NULL }, }
{ "1E25 ('ABCD')", NULL }, },
{ "1e25 ('ABCD')", NULL },*/ { "0.8 [0:3] ('ABCDEFG' & 'abcdefg')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ABCDEFG"),
T(Ampersand),
S("abcdefg"),
T(RightParen)
}
},
{ "1.0 ('ABCD') | ('EFGH')", 8,
{ F(1.0),
T(LeftParen),
S("ABCD"),
T(RightParen),
T(Divider),
T(LeftParen),
S("EFGH"),
T(RightParen)
}
},
{ "1.0 [0:3] ('ABCD') | [2:4] ('EFGH')", 18,
{ F(1.0),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ABCD"),
T(RightParen),
T(Divider),
T(LeftBracket),
I(2),
T(Colon),
I(4),
T(RightBracket),
T(LeftParen),
S("EFGH"),
T(RightParen)
}
},
{ "0.8 [0:4] (\\077Mkj0x34 & 'abcdefgh')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(4),
T(RightBracket),
T(LeftParen),
S("\077Mkj0x34"),
T(Ampersand),
S("abcdefgh"),
T(RightParen)
}
},
{ "0.8 [0:4] (\\077Mkj\\x34 & 'abcdefgh')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(4),
T(RightBracket),
T(LeftParen),
S("\077Mkj\x34"),
T(Ampersand),
S("abcdefgh"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\077034 & 'abcd')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\077034"),
T(Ampersand),
S("abcd"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\077\\034 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\077\034"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\77\\034 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\077\034"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\7 & 'a')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\007"),
T(Ampersand),
S("a"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"\\17\" & 'a')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\017"),
T(Ampersand),
S("a"),
T(RightParen)
}
},
{ "0.8 [0:3] ('\\17' & 'a')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\017"),
T(Ampersand),
S("a"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\g & 'a')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("g"),
T(Ampersand),
S("a"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\g&\\b)", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("g"),
T(Ampersand),
S("\b"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\g\\&b & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("g&b"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [0:3] (0x3457 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\x34\x57"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\x34\\x57 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\x34\x57"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (0xA4b7 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\xA4\xb7"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\\xA4\\xb7 & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\xA4\xb7"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"\\xA4\\xb7\" & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\xA4\xb7"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] (\'\\xA4\\xb7\' & 'ab')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("\xA4\xb7"),
T(Ampersand),
S("ab"),
T(RightParen)
}
},
{ "0.8 [0:3] ('ab\"' & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ab\""),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"ab\\\"\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ab\""),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"ab\\A\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("abA"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"ab'\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ab'"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [0:3] (\"ab\\\\\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(0),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("ab\\"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [-5:-3] (\"abc\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(-5),
T(Colon),
I(-3),
T(RightBracket),
T(LeftParen),
S("abc"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "0.8 [5:3] (\"abc\" & 'abc')", 11,
{ F(0.8),
T(LeftBracket),
I(5),
T(Colon),
I(3),
T(RightBracket),
T(LeftParen),
S("abc"),
T(Ampersand),
S("abc"),
T(RightParen)
}
},
{ "1.2 ('ABCD')", 4,
{ F(1.2),
T(LeftParen),
S("ABCD"),
T(RightParen)
}
},
{ ".2 ('ABCD')", 4,
{ F(0.2),
T(LeftParen),
S("ABCD"),
T(RightParen)
}
},
{ "0. ('ABCD')", 4,
{ F(0.0),
T(LeftParen),
S("ABCD"),
T(RightParen)
}
},
{ "-1 ('ABCD')", 4,
{ I(-1),
T(LeftParen),
S("ABCD"),
T(RightParen)
}
},
{ "+1 ('ABCD')", 4,
{ I(1),
T(LeftParen),
S("ABCD"),
T(RightParen)
}
}
// e notation is no longer supported, due to the fact that it's
// not useful in the context of a priority...
// { "1E25 ('ABCD')", NULL },
// { "1e25 ('ABCD')", NULL }
}; };
// Undefine our nasty macros // Undefine our nasty macros
@@ -129,13 +549,27 @@ ParserTest::ScannerTest() {
const int testCaseCount = sizeof(testCases) / sizeof(test_case); const int testCaseCount = sizeof(testCases) / sizeof(test_case);
for (int i = 0; i < testCaseCount; i++) { for (int i = 0; i < testCaseCount; i++) {
// cout << testCases[i].rule << endl;
NextSubTest(); NextSubTest();
TokenStream stream(testCases[i].rule); TokenStream stream;
try {
stream.SetTo(testCases[i].rule);
} catch (Err *e) {
CppUnit::Exception *err = new CppUnit::Exception(e->Msg());
delete e;
throw *err;
}
CHK(stream.InitCheck() == B_OK); CHK(stream.InitCheck() == B_OK);
for (int j = 0; j < testCases[i].tokenCount; j++) { for (int j = 0; j < testCases[i].tokenCount; j++) {
Token *token = stream.Get(); Token *token = stream.Get();
CHK(token); CHK(token);
// cout << tokenTypeToString(token->Type()) << endl; // cout << tokenTypeToString(token->Type()) << endl;
/*
if (token->Type() == CharacterString)
cout << " token1 == " << token->String() << endl;
if (testCases[i].tokens[j]->Type() == CharacterString)
cout << " token2 == " << (testCases[i].tokens[j])->String() << endl;
*/
CHK(*token == *(testCases[i].tokens[j])); CHK(*token == *(testCases[i].tokens[j]));
/* /*
switch (token->Type()) { switch (token->Type()) {
@@ -153,8 +587,8 @@ ParserTest::ScannerTest() {
delete testCases[i].tokens[j]; delete testCases[i].tokens[j];
} }
CHK(stream.IsEmpty()); CHK(stream.IsEmpty());
// cout << endl;
} }
//
#else // !TEST_R5 #else // !TEST_R5
Outputf("(no tests actually performed for R5 version)\n"); Outputf("(no tests actually performed for R5 version)\n");
#endif // !TEST_R5 #endif // !TEST_R5