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) =
nullptr;
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];
315 else if (cur ==
'\\')
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);
436 d->nextState.setToken(cur ==
'<' ? TK_ShiftLeft : TK_ShiftRight);
438 else if (cur ==
'#' && next ==
'#')
439 d->nextState.setToken(TK_MacroConcat);
440 else if (cur ==
':' && next ==
':')
441 d->nextState.setToken(TK_ScopeResolution);
442 else if (cur ==
'+' && next ==
'+')
443 d->nextState.setToken(TK_Increment);
447 d->nextState.setToken(TK_Decrement);
448 else if (next ==
'>')
449 d->nextState.setToken(TK_PointerMember);
451 else if (cur ==
'.' && next ==
'.' &&
452 d->scanPos + 1 < d->length && d->data[d->scanPos + 1] ==
'.')
454 d->nextState.setToken(TK_Ellipsis);
457 else if (next ==
'=')
462 d->nextState.setToken(TK_EqEq);
465 d->nextState.setToken(TK_NotEq);
468 d->nextState.setToken(TK_GtrEq);
471 d->nextState.setToken(TK_LessEq);
474 d->nextState.setToken(TK_AddEq);
477 d->nextState.setToken(TK_SubEq);
480 d->nextState.setToken(TK_MulEq);
483 d->nextState.setToken(TK_DivEq);
486 d->nextState.setToken(TK_ModEq);
489 d->nextState.setToken(TK_AndEq);
492 d->nextState.setToken(TK_OrEq);
495 d->nextState.setToken(TK_XorEq);
502 if (d->nextState.token() != cur)
512 while (d->scanPos < d->length)
514 cur = d->data[d->scanPos];
515 switch (d->nextState.token())
520 if (cur !=
'_' && (cur < 'A' || cur >
'Z') && (cur < 'a' || cur >
'z') && (cur < '0' || cur >
'9'))
524 if (cur ==
'.' || (d->scanPos - 1 != start && cur ==
'e'))
525 d->nextState.setToken(TK_FloatConst);
526 else if ((cur ==
'x' || cur ==
'X') && d->scanPos - 1 == start)
536 if (cur < '0' || cur >
'9')
540 if (cur < '0' || cur >
'7')
544 if ((cur < '0' || cur >
'9') && (cur < 'A' || cur >
'F') && (cur < 'a' || cur >
'f'))
550 [[gnu::fallthrough]];
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;
583 else if (cur ==
'\\')
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(
nullptr));
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(
nullptr, 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(
unsigned 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[static_cast<unsigned>(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[static_cast<unsigned>(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)
713 void Scanner::setScriptIdentifier(
const QString &ident)
715 d->scriptIdentifier = ident;
718 int Scanner::skipLine()
720 int ret = currentPos();
721 while (d->logicalPosition < d->length)
723 char thisChar = d->data[d->logicalPosition];
724 char nextChar = d->logicalPosition + 1 < d->length ? d->data[d->logicalPosition + 1] : 0;
725 if (thisChar ==
'\n' || thisChar ==
'\r')
727 ret = d->logicalPosition++;
728 if (nextChar ==
'\r')
729 d->logicalPosition++;
734 d->logicalPosition++;
736 if (d->logicalPosition > d->scanPos)
738 d->scanPos = d->logicalPosition;
741 d->logicalPosition = d->scanPos;
758 return d->scanPos < d->length;
763 static char escapeCharacters[] = {
'\\',
'"', 0};
764 const QString &Scanner::escape(QString &str)
766 for (
unsigned int i = 0; escapeCharacters[i] != 0; i++)
769 for (
int p = 0; p < str.length() && (p = str.indexOf(escapeCharacters[i], p)) != -1; p += 2)
776 const QString &Scanner::unescape(QString &str)
778 for (
unsigned int i = 0; escapeCharacters[i] != 0; i++)
780 QString sequence =
"\\" + QString(escapeCharacters[i]);
781 for (
int p = 0; p < str.length() && (p = str.indexOf(sequence, p)) != -1; p++)
782 str.replace(str.indexOf(sequence, p), 2, escapeCharacters[i]);
788 Scanner::ParserState::ParserState()
792 Scanner::ParserState::~ParserState()
796 const QString &Scanner::ParserState::str()
const 801 void Scanner::ParserState::setStr(
const QString &v)
806 unsigned int Scanner::ParserState::number()
const 811 void Scanner::ParserState::setNumber(
unsigned int v)
816 double Scanner::ParserState::decimal()
const 821 void Scanner::ParserState::setDecimal(
double v)
826 bool Scanner::ParserState::boolean()
const 831 void Scanner::ParserState::setBoolean(
bool v)
836 char Scanner::ParserState::token()
const 841 void Scanner::ParserState::setToken(
char v)
846 unsigned int Scanner::ParserState::tokenLine()
const 851 void Scanner::ParserState::setTokenLine(
unsigned int v)
856 unsigned int Scanner::ParserState::tokenLinePosition()
const 858 return d->tokenLinePosition;
861 void Scanner::ParserState::setTokenLinePosition(
unsigned int v)
863 d->tokenLinePosition = v;
866 unsigned int Scanner::ParserState::scanPos()
const 871 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.