39 unsigned int lineStart;
40 unsigned int logicalPosition;
45 QString scriptIdentifier;
48 DClass<Scanner::ParserState>
56 unsigned int tokenLine;
57 unsigned int tokenLinePosition;
64 void (*
Scanner::messageHandler)(MessageLevel, const
char*, va_list) = NULL;
66 static const
char* const TokenNames[TK_NumSpecialTokens] =
77 "Greater Than or Equals" 78 "Less Than or Equals",
85 "Macro Concatenation",
95 "Assign Exclusive Or",
101 Scanner::Scanner(
const char* data,
int length)
105 d->logicalPosition = 0;
109 length = strlen(data);
111 d->data =
new char[length];
112 memcpy(d->data, data, length);
116 d->state.setScanPos(d->scanPos);
127 void Scanner::checkForMeta()
129 if(d->scanPos+10 < d->length)
132 memcpy(metaCheck, d->data+d->scanPos, 7);
134 if(strcmp(metaCheck,
"/*meta:") == 0)
137 int metaStart = d->scanPos;
140 while(d->scanPos < d->length)
142 char thisChar = d->data[d->scanPos];
143 char nextChar = d->scanPos+1 < d->length ? d->data[d->scanPos+1] : 0;
144 if(thisChar ==
'*' && nextChar ==
'/')
146 lineLength = d->scanPos-metaStart-1-fileLength;
150 if(thisChar ==
':' && fileLength == 0)
151 fileLength = d->scanPos-metaStart;
154 if(fileLength > 0 && lineLength > 0)
156 setScriptIdentifier(QString::fromUtf8(d->data+metaStart, fileLength));
157 QString lineNumber = QString::fromUtf8(d->data+metaStart+fileLength+1, lineLength);
158 d->line = atoi(lineNumber.toUtf8().constData());
159 d->lineStart = d->scanPos;
168 while(d->scanPos < d->length)
170 char cur = d->data[d->scanPos];
171 char next = d->scanPos+1 < d->length ? d->data[d->scanPos+1] : 0;
174 if(cur !=
'*' || next !=
'/')
176 if(cur ==
'\n' || cur ==
'\r')
183 if(cur ==
'\r' && next ==
'\n')
198 if(cur ==
' ' || cur ==
'\t' || cur == 0)
200 else if(cur ==
'\n' || cur ==
'\r')
207 if(cur ==
'\r' && next ==
'\n')
212 else if(cur ==
'/' && comment == 0)
246 if(d->nextState.token() == token || (d->nextState.token() == TK_IntConst && token == TK_FloatConst))
256 int Scanner::currentLine()
const 258 return d->state.tokenLine();
261 int Scanner::currentLinePos()
const 263 return d->state.tokenLinePosition();
266 int Scanner::currentPos()
const 268 return d->logicalPosition;
271 unsigned int Scanner::currentScanPos()
const 278 d->scanPos = d->nextState.scanPos();
279 d->logicalPosition = d->scanPos;
282 d->prevState = d->state;
283 d->state = d->nextState;
289 d->lineStart = d->scanPos;
292 bool Scanner::nextString()
294 d->nextState.setTokenLine(d->line);
295 d->nextState.setTokenLinePosition(d->scanPos - d->lineStart);
296 d->nextState.setToken(TK_NoToken);
298 d->scanPos = d->state.scanPos();
300 if(d->scanPos >= d->length)
303 int start = d->scanPos;
304 int end = d->scanPos;
305 bool quoted = d->data[d->scanPos] ==
'"';
310 while(d->scanPos < d->length)
312 char cur = d->data[d->scanPos];
327 while(d->scanPos < d->length)
329 char cur = d->data[d->scanPos];
345 if(d->scanPos == d->length)
350 d->nextState.setScanPos(d->scanPos);
351 QString thisString = QString::fromUtf8(d->data+start, end-start);
353 unescape(thisString);
354 d->nextState.setStr(thisString);
355 d->nextState.setToken(TK_StringConst);
374 d->nextState.setTokenLine(d->line);
375 d->nextState.setTokenLinePosition(d->scanPos - d->lineStart);
376 d->nextState.setToken(TK_NoToken);
377 if(d->scanPos >= d->length)
384 unsigned int start = d->scanPos;
385 unsigned int end = d->scanPos;
386 int integerBase = 10;
387 bool floatHasDecimal =
false;
388 bool floatHasExponent =
false;
389 bool stringFinished =
false;
391 char cur = d->data[d->scanPos++];
393 if(cur ==
'_' || (cur >=
'A' && cur <=
'Z') || (cur >=
'a' && cur <=
'z'))
394 d->nextState.setToken(TK_Identifier);
395 else if(cur >=
'0' && cur <=
'9')
399 d->nextState.setToken(TK_IntConst);
401 else if(cur ==
'.' && d->scanPos < d->length && d->data[d->scanPos] !=
'.')
403 floatHasDecimal =
true;
404 d->nextState.setToken(TK_FloatConst);
409 d->nextState.setToken(TK_StringConst);
414 d->nextState.setToken(cur);
417 if(d->scanPos < d->length)
419 char next = d->data[d->scanPos];
420 if(cur ==
'&' && next ==
'&')
421 d->nextState.setToken(TK_AndAnd);
422 else if(cur ==
'|' && next ==
'|')
423 d->nextState.setToken(TK_OrOr);
425 (cur ==
'<' && next ==
'<') ||
426 (cur ==
'>' && next ==
'>')
430 if(d->scanPos+1 > d->length && d->data[d->scanPos+1] ==
'=')
433 d->nextState.setToken(cur ==
'<' ? TK_ShiftLeftEq : TK_ShiftRightEq);
437 d->nextState.setToken(cur ==
'<' ? TK_ShiftLeft : TK_ShiftRight);
439 else if(cur ==
'#' && next ==
'#')
440 d->nextState.setToken(TK_MacroConcat);
441 else if(cur ==
':' && next ==
':')
442 d->nextState.setToken(TK_ScopeResolution);
443 else if(cur ==
'+' && next ==
'+')
444 d->nextState.setToken(TK_Increment);
448 d->nextState.setToken(TK_Decrement);
450 d->nextState.setToken(TK_PointerMember);
452 else if(cur ==
'.' && next ==
'.' &&
453 d->scanPos+1 < d->length && d->data[d->scanPos+1] ==
'.')
455 d->nextState.setToken(TK_Ellipsis);
463 d->nextState.setToken(TK_EqEq);
466 d->nextState.setToken(TK_NotEq);
469 d->nextState.setToken(TK_GtrEq);
472 d->nextState.setToken(TK_LessEq);
475 d->nextState.setToken(TK_AddEq);
478 d->nextState.setToken(TK_SubEq);
481 d->nextState.setToken(TK_MulEq);
484 d->nextState.setToken(TK_DivEq);
487 d->nextState.setToken(TK_ModEq);
490 d->nextState.setToken(TK_AndEq);
493 d->nextState.setToken(TK_OrEq);
496 d->nextState.setToken(TK_XorEq);
503 if(d->nextState.token() != cur)
513 while(d->scanPos < d->length)
515 cur = d->data[d->scanPos];
516 switch(d->nextState.token())
521 if(cur !=
'_' && (cur < 'A' || cur >
'Z') && (cur < 'a' || cur >
'z') && (cur < '0' || cur >
'9'))
525 if(cur ==
'.' || (d->scanPos-1 != start && cur ==
'e'))
526 d->nextState.setToken(TK_FloatConst);
527 else if((cur ==
'x' || cur ==
'X') && d->scanPos-1 == start)
537 if(cur < '0' || cur >
'9')
541 if(cur < '0' || cur >
'7')
545 if((cur < '0' || cur >
'9') && (cur < 'A' || cur >
'F') && (cur < 'a' || cur >
'f'))
552 if(cur < '0' || cur >
'9')
554 if(!floatHasDecimal && cur ==
'.')
556 floatHasDecimal =
true;
559 else if(!floatHasExponent && cur ==
'e')
561 floatHasDecimal =
true;
562 floatHasExponent =
true;
563 if(d->scanPos+1 < d->length)
565 char next = d->data[d->scanPos+1];
566 if((next < '0' || next >
'9') && next !=
'+' && next !=
'-')
579 stringFinished =
true;
587 if(start == end && !stringFinished)
593 if(d->scanPos == d->length && !stringFinished)
597 d->nextState.setScanPos(d->scanPos);
598 if(end-start > 0 || stringFinished)
600 d->nextState.setStr(QByteArray(d->data+start, end-start));
601 if(d->nextState.token() == TK_FloatConst)
603 if(floatHasDecimal && d->nextState.str().length() == 1)
606 d->nextState.setToken(
'.');
610 d->nextState.setDecimal(d->nextState.str().toDouble(NULL));
611 d->nextState.setNumber(static_cast<int> (d->nextState.decimal()));
612 d->nextState.setBoolean(d->nextState.number() != 0);
615 else if(d->nextState.token() == TK_IntConst)
617 d->nextState.setNumber(d->nextState.str().toUInt(NULL, integerBase));
618 d->nextState.setDecimal(d->nextState.number());
619 d->nextState.setBoolean(d->nextState.number() != 0);
621 else if(d->nextState.token() == TK_Identifier)
624 if(d->nextState.str().compare(
"true") == 0)
626 d->nextState.setToken(TK_BoolConst);
627 d->nextState.setBoolean(
true);
629 else if(d->nextState.str().compare(
"false") == 0)
631 d->nextState.setToken(TK_BoolConst);
632 d->nextState.setBoolean(
false);
635 else if(d->nextState.token() == TK_StringConst)
637 QString str = d->nextState.str();
638 d->nextState.setStr(unescape(str));
644 d->nextState.setToken(TK_NoToken);
650 void Scanner::mustGetToken(
char token)
655 if(token < TK_NumSpecialTokens && d->state.token() < TK_NumSpecialTokens)
656 scriptMessage(Scanner::ML_ERROR,
"Expected '%s' but got '%s' instead.", TokenNames[token], TokenNames[d->state.token()]);
657 else if(token < TK_NumSpecialTokens && d->state.token() >= TK_NumSpecialTokens)
658 scriptMessage(Scanner::ML_ERROR,
"Expected '%s' but got '%c' instead.", TokenNames[token], d->state.token());
659 else if(token >= TK_NumSpecialTokens && d->state.token() < TK_NumSpecialTokens)
660 scriptMessage(Scanner::ML_ERROR,
"Expected '%c' but got '%s' instead.", token, TokenNames[d->state.token()]);
662 scriptMessage(Scanner::ML_ERROR,
"Expected '%c' but got '%c' instead.", token, d->state.token());
666 void Scanner::rewind()
669 d->nextState = d->state;
670 d->state = d->prevState;
671 d->scanPos = d->state.scanPos();
673 d->line = d->prevState.tokenLine();
674 d->logicalPosition = d->prevState.tokenLinePosition();
682 void Scanner::scriptMessage(MessageLevel level,
const char* error, ...)
const 684 const char* messageLevel;
688 messageLevel =
"Notice";
691 messageLevel =
"Warning";
694 messageLevel =
"Error";
698 char* newMessage =
new char[strlen(error) + d->scriptIdentifier.length() + 25];
699 sprintf(newMessage,
"%s:%d:%d:%s: %s\n", d->scriptIdentifier.toUtf8().constData(), currentLine(), currentLinePos(), messageLevel, error);
701 va_start(list, error);
703 messageHandler(level, newMessage, list);
705 vfprintf(stderr, newMessage, list);
709 if(!messageHandler && level == ML_ERROR)
714 void Scanner::setScriptIdentifier(
const QString &ident)
716 d->scriptIdentifier = ident;
719 int Scanner::skipLine()
721 int ret = currentPos();
722 while(d->logicalPosition < d->length)
724 char thisChar = d->data[d->logicalPosition];
725 char nextChar = d->logicalPosition+1 < d->length ? d->data[d->logicalPosition+1] : 0;
726 if(thisChar ==
'\n' || thisChar ==
'\r')
728 ret = d->logicalPosition++;
730 d->logicalPosition++;
735 d->logicalPosition++;
737 if(d->logicalPosition > d->scanPos)
739 d->scanPos = d->logicalPosition;
742 d->logicalPosition = d->scanPos;
759 return d->scanPos < d->length;
764 static char escapeCharacters[] = {
'\\',
'"', 0};
765 const QString& Scanner::escape(QString &str)
767 for(
unsigned int i = 0;escapeCharacters[i] != 0;i++)
770 for(
int p = 0;p < str.length() && (p = str.indexOf(escapeCharacters[i], p)) != -1;p += 2)
777 const QString& Scanner::unescape(QString &str)
779 for(
unsigned int i = 0;escapeCharacters[i] != 0;i++)
781 QString sequence =
"\\" + QString(escapeCharacters[i]);
782 for(
int p = 0;p < str.length() && (p = str.indexOf(sequence, p)) != -1;p++)
783 str.replace(str.indexOf(sequence, p), 2, escapeCharacters[i]);
789 Scanner::ParserState::ParserState()
793 Scanner::ParserState::~ParserState()
797 const QString &Scanner::ParserState::str()
const 802 void Scanner::ParserState::setStr(
const QString &v)
807 unsigned int Scanner::ParserState::number()
const 812 void Scanner::ParserState::setNumber(
unsigned int v)
817 double Scanner::ParserState::decimal()
const 822 void Scanner::ParserState::setDecimal(
double v)
827 bool Scanner::ParserState::boolean()
const 832 void Scanner::ParserState::setBoolean(
bool v)
837 char Scanner::ParserState::token()
const 842 void Scanner::ParserState::setToken(
char v)
847 unsigned int Scanner::ParserState::tokenLine()
const 852 void Scanner::ParserState::setTokenLine(
unsigned int v)
857 unsigned int Scanner::ParserState::tokenLinePosition()
const 859 return d->tokenLinePosition;
862 void Scanner::ParserState::setTokenLinePosition(
unsigned int v)
864 d->tokenLinePosition = v;
867 unsigned int Scanner::ParserState::scanPos()
const 872 void Scanner::ParserState::setScanPos(
unsigned int v)
bool checkToken(char token)
void checkForWhitespace()
Scanner reads scripts by checking individual tokens.
bool nextToken(bool autoExpandState=true)
const char * scriptData() const
Only can rewind one step.