aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4codegen.cpp43
-rw-r--r--src/qml/compiler/qv4codegen_p.h1
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp11
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h1
-rw-r--r--src/qml/parser/qqmljs.g51
-rw-r--r--src/qml/parser/qqmljsast.cpp8
-rw-r--r--src/qml/parser/qqmljsast_p.h24
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h1
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h3
-rw-r--r--src/qml/parser/qqmljslexer.cpp324
-rw-r--r--src/qml/parser/qqmljslexer_p.h12
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp64
12 files changed, 405 insertions, 138 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 8ada1d505e..bf481f351f 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1933,6 +1933,49 @@ bool Codegen::visit(StringLiteral *ast)
return false;
}
+bool Codegen::visit(TemplateLiteral *ast)
+{
+ if (hasError)
+ return false;
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
+
+ if (ast->expression) {
+ RegisterScope scope(this);
+ int temp = bytecodeGenerator->newRegister();
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
+
+ Reference expr = expression(ast->expression);
+
+ if (ast->next) {
+ int temp2 = bytecodeGenerator->newRegister();
+ expr.storeOnStack(temp2);
+ visit(ast->next);
+
+ Instruction::Add instr;
+ instr.lhs = temp2;
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ }
+
+ Instruction::Add instr;
+ instr.lhs = temp;
+ bytecodeGenerator->addInstruction(instr);
+ }
+
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+
+ _expr.setResult(r);
+ return false;
+
+}
+
bool Codegen::visit(ThisExpression *)
{
if (hasError)
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index d51dc29517..61029644c3 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -576,6 +576,7 @@ protected:
bool visit(AST::PreIncrementExpression *ast) override;
bool visit(AST::RegExpLiteral *ast) override;
bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::TildeExpression *ast) override;
bool visit(AST::TrueLiteral *ast) override;
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 84ee452332..f985c74f9d 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -257,6 +257,17 @@ bool ScanFunctions::visit(FunctionExpression *ast)
return true;
}
+bool ScanFunctions::visit(TemplateLiteral *ast)
+{
+ while (ast) {
+ if (ast->expression)
+ Node::accept(ast->expression, this);
+ ast = ast->next;
+ }
+ return true;
+
+}
+
void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
{
if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 87b7210879..0494a3248d 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -110,6 +110,7 @@ protected:
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::ExpressionStatement *ast) override;
bool visit(AST::FunctionExpression *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
void enterFunction(AST::FunctionExpression *ast, bool enterName);
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 60a10bee6b..5313d4d066 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -79,6 +79,12 @@
%token T_COMPATIBILITY_SEMICOLON
%token T_ENUM "enum"
+--- template strings
+%token T_NO_SUBSTITUTION_TEMPLATE
+%token T_TEMPLATE_HEAD
+%token T_TEMPLATE_MIDDLE
+%token T_TEMPLATE_TAIL
+
--- context keywords.
%token T_PUBLIC "public"
%token T_IMPORT "import"
@@ -250,6 +256,7 @@ public:
AST::ElementList *ElementList;
AST::Elision *Elision;
AST::ExpressionNode *Expression;
+ AST::TemplateLiteral *Template;
AST::Finally *Finally;
AST::FormalParameterList *FormalParameterList;
AST::FunctionBody *FunctionBody;
@@ -1342,6 +1349,37 @@ case $rule_number: {
} break;
./
+PrimaryExpression: TemplateLiteral ;
+/.
+case $rule_number:
+ // nothing that needs to be done here
+ break;
+./
+
+TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE ;
+/. case $rule_number: ./
+
+TemplateSpans: T_TEMPLATE_TAIL ;
+/.
+case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), nullptr);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+TemplateSpans: T_TEMPLATE_MIDDLE Expression TemplateSpans;
+/. case $rule_number: ./
+
+TemplateLiteral: T_TEMPLATE_HEAD Expression TemplateSpans ;
+/. case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), sym(2).Expression);
+ node->next = sym(3).Template;
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
PrimaryExpression: T_DIVIDE_ ;
/:
#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
@@ -1721,6 +1759,13 @@ case $rule_number: {
} break;
./
+MemberExpression: MemberExpression TemplateLiteral ;
+/.
+case $rule_number: {
+ qWarning() << "Template member expression implemented";
+} break;
+./
+
NewExpression: MemberExpression ;
NewExpression: T_NEW NewExpression ;
@@ -1772,6 +1817,12 @@ case $rule_number: {
} break;
./
+CallExpression: CallExpression TemplateLiteral;
+/. case $rule_number: {
+ qWarning() << "Template calling not implemented";
+} break;
+./
+
ArgumentListOpt: ;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 34657a7d48..6fdfd8279c 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -155,6 +155,14 @@ void StringLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void TemplateLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
void NumericLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index ed3c83badf..5f094ad4f5 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -188,6 +188,7 @@ public:
Kind_StringLiteral,
Kind_StringLiteralPropertyName,
Kind_SwitchStatement,
+ Kind_TemplateLiteral,
Kind_ThisExpression,
Kind_ThrowStatement,
Kind_TildeExpression,
@@ -428,6 +429,29 @@ public:
SourceLocation literalToken;
};
+class QML_PARSER_EXPORT TemplateLiteral : public ExpressionNode
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TemplateLiteral)
+
+ TemplateLiteral(const QStringRef &str, ExpressionNode *e)
+ : value(str), expression(e), next(nullptr)
+ { kind = K; }
+
+ SourceLocation firstSourceLocation() const override
+ { return literalToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : literalToken; }
+
+ void accept0(Visitor *visitor) override;
+
+ QStringRef value;
+ ExpressionNode *expression;
+ SourceLocation literalToken;
+ TemplateLiteral *next;
+};
+
class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode
{
public:
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 140a757e51..3ebef5b128 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -91,6 +91,7 @@ class TrueLiteral;
class FalseLiteral;
class NumericLiteral;
class StringLiteral;
+class TemplateLiteral;
class RegExpLiteral;
class ArrayLiteral;
class ObjectLiteral;
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index 13218f0e98..5a0767a697 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -125,6 +125,9 @@ public:
virtual bool visit(StringLiteral *) { return true; }
virtual void endVisit(StringLiteral *) {}
+ virtual bool visit(TemplateLiteral *) { return true; }
+ virtual void endVisit(TemplateLiteral *) {}
+
virtual bool visit(NumericLiteral *) { return true; }
virtual void endVisit(NumericLiteral *) {}
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index 53a8d593da..d48766e9a8 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -242,6 +242,7 @@ int Lexer::lex()
{
const int previousTokenKind = _tokenKind;
+ again:
_tokenSpell = QStringRef();
_tokenKind = scanToken();
_tokenLength = _codePtr - _tokenStartPtr - 1;
@@ -253,6 +254,9 @@ int Lexer::lex()
// update the flags
switch (_tokenKind) {
case T_LBRACE:
+ if (_bracesCount > 0)
+ ++_bracesCount;
+ Q_FALLTHROUGH();
case T_SEMICOLON:
case T_QUESTION:
case T_COLON:
@@ -283,6 +287,11 @@ int Lexer::lex()
case T_THROW:
_restrictedKeyword = true;
break;
+ case T_RBRACE:
+ if (_bracesCount > 0)
+ --_bracesCount;
+ if (_bracesCount == 0)
+ goto again;
} // switch
// update the parentheses state
@@ -444,6 +453,12 @@ int Lexer::scanToken()
return tk;
}
+ if (_bracesCount == 0) {
+ // we're inside a Template string
+ return scanString(TemplateContinuation);
+ }
+
+
_terminator = false;
again:
@@ -664,145 +679,12 @@ again:
}
return T_NOT;
+ case '`':
+ _outerTemplateBraceCount.push(_bracesCount);
+ Q_FALLTHROUGH();
case '\'':
- case '"': {
- const QChar quote = ch;
- bool multilineStringLiteral = false;
-
- const QChar *startCode = _codePtr;
-
- if (_engine) {
- while (_codePtr <= _endPtr) {
- if (isLineTerminator()) {
- if (qmlMode())
- break;
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
- return T_ERROR;
- } else if (_char == QLatin1Char('\\')) {
- break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
- scanChar();
-
- return T_STRING_LITERAL;
- }
- scanChar();
- }
- }
-
- _validTokenText = true;
- _tokenText.resize(0);
- startCode--;
- while (startCode != _codePtr - 1)
- _tokenText += *startCode++;
-
- while (_codePtr <= _endPtr) {
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- multilineStringLiteral = true;
- _tokenText += _char;
- if (sequenceLength == 2)
- _tokenText += *_codePtr;
- scanChar();
- } else if (_char == quote) {
- scanChar();
-
- if (_engine)
- _tokenSpell = _engine->newStringRef(_tokenText);
-
- return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
- } else if (_char == QLatin1Char('\\')) {
- scanChar();
- if (_codePtr > _endPtr) {
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
- return T_ERROR;
- }
-
- QChar u;
-
- switch (_char.unicode()) {
- // unicode escape sequence
- case 'u': {
- bool ok = false;
- uint codePoint = decodeUnicodeEscapeCharacter(&ok);
- if (!ok)
- return T_ERROR;
- if (QChar::requiresSurrogates(codePoint)) {
- // need to use a surrogate pair
- _tokenText += QChar(QChar::highSurrogate(codePoint));
- u = QChar::lowSurrogate(codePoint);
- } else {
- u = codePoint;
- }
- } break;
-
- // hex escape sequence
- case 'x': {
- bool ok = false;
- u = decodeHexEscapeCharacter(&ok);
- if (!ok) {
- _errorCode = IllegalHexadecimalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
- return T_ERROR;
- }
- } break;
-
- // single character escape sequence
- case '\\': u = QLatin1Char('\\'); scanChar(); break;
- case '\'': u = QLatin1Char('\''); scanChar(); break;
- case '\"': u = QLatin1Char('\"'); scanChar(); break;
- case 'b': u = QLatin1Char('\b'); scanChar(); break;
- case 'f': u = QLatin1Char('\f'); scanChar(); break;
- case 'n': u = QLatin1Char('\n'); scanChar(); break;
- case 'r': u = QLatin1Char('\r'); scanChar(); break;
- case 't': u = QLatin1Char('\t'); scanChar(); break;
- case 'v': u = QLatin1Char('\v'); scanChar(); break;
-
- case '0':
- if (! _codePtr->isDigit()) {
- scanChar();
- u = QLatin1Char('\0');
- break;
- }
- Q_FALLTHROUGH();
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
- return T_ERROR;
-
- case '\r':
- case '\n':
- case 0x2028u:
- case 0x2029u:
- scanChar();
- continue;
-
- default:
- // non escape character
- u = _char;
- scanChar();
- }
-
- _tokenText += u;
- } else {
- _tokenText += _char;
- scanChar();
- }
- }
-
- _errorCode = UnclosedStringLiteral;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
- return T_ERROR;
- }
+ case '"':
+ return scanString(ScanStringMode(ch.unicode()));
case '0':
case '1':
case '2':
@@ -910,6 +792,172 @@ again:
return T_ERROR;
}
+int Lexer::scanString(ScanStringMode mode)
+{
+ QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
+ bool multilineStringLiteral = false;
+
+ const QChar *startCode = _codePtr;
+
+ if (_engine) {
+ while (_codePtr <= _endPtr) {
+ if (isLineTerminator()) {
+ if (qmlMode())
+ break;
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
+ return T_ERROR;
+ } else if (_char == QLatin1Char('\\')) {
+ break;
+ } else if (_char == '$' && quote == QLatin1Char('`')) {
+ break;
+ } else if (_char == quote) {
+ _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
+ scanChar();
+
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
+
+ if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+ else if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else
+ return T_STRING_LITERAL;
+ }
+ scanChar();
+ }
+ }
+
+ _validTokenText = true;
+ _tokenText.resize(0);
+ startCode--;
+ while (startCode != _codePtr - 1)
+ _tokenText += *startCode++;
+
+ while (_codePtr <= _endPtr) {
+ if (unsigned sequenceLength = isLineTerminatorSequence()) {
+ multilineStringLiteral = true;
+ _tokenText += _char;
+ if (sequenceLength == 2)
+ _tokenText += *_codePtr;
+ scanChar();
+ } else if (_char == mode) {
+ scanChar();
+
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
+
+ if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+
+ return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
+ } else if (quote == QLatin1Char('`') && _char == QLatin1Char('$') && *_codePtr == '{') {
+ scanChar();
+ scanChar();
+ _bracesCount = 1;
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+
+ return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
+ } else if (_char == QLatin1Char('\\')) {
+ scanChar();
+ if (_codePtr > _endPtr) {
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ return T_ERROR;
+ }
+
+ QChar u;
+
+ switch (_char.unicode()) {
+ // unicode escape sequence
+ case 'u': {
+ bool ok = false;
+ uint codePoint = decodeUnicodeEscapeCharacter(&ok);
+ if (!ok)
+ return T_ERROR;
+ if (QChar::requiresSurrogates(codePoint)) {
+ // need to use a surrogate pair
+ _tokenText += QChar(QChar::highSurrogate(codePoint));
+ u = QChar::lowSurrogate(codePoint);
+ } else {
+ u = codePoint;
+ }
+ } break;
+
+ // hex escape sequence
+ case 'x': {
+ bool ok = false;
+ u = decodeHexEscapeCharacter(&ok);
+ if (!ok) {
+ _errorCode = IllegalHexadecimalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
+ return T_ERROR;
+ }
+ } break;
+
+ // single character escape sequence
+ case '\\': u = QLatin1Char('\\'); scanChar(); break;
+ case '\'': u = QLatin1Char('\''); scanChar(); break;
+ case '\"': u = QLatin1Char('\"'); scanChar(); break;
+ case 'b': u = QLatin1Char('\b'); scanChar(); break;
+ case 'f': u = QLatin1Char('\f'); scanChar(); break;
+ case 'n': u = QLatin1Char('\n'); scanChar(); break;
+ case 'r': u = QLatin1Char('\r'); scanChar(); break;
+ case 't': u = QLatin1Char('\t'); scanChar(); break;
+ case 'v': u = QLatin1Char('\v'); scanChar(); break;
+
+ case '0':
+ if (! _codePtr->isDigit()) {
+ scanChar();
+ u = QLatin1Char('\0');
+ break;
+ }
+ Q_FALLTHROUGH();
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
+ return T_ERROR;
+
+ case '\r':
+ case '\n':
+ case 0x2028u:
+ case 0x2029u:
+ scanChar();
+ continue;
+
+ default:
+ // non escape character
+ u = _char;
+ scanChar();
+ }
+
+ _tokenText += u;
+ } else {
+ _tokenText += _char;
+ scanChar();
+ }
+ }
+
+ _errorCode = UnclosedStringLiteral;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
+ return T_ERROR;
+}
+
int Lexer::scanNumber(QChar ch)
{
if (ch == QLatin1Char('0')) {
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 71a4a27dff..75afdf0e02 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -55,6 +55,7 @@
#include <private/qqmljsgrammar_p.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
QT_QML_BEGIN_NAMESPACE
@@ -167,6 +168,13 @@ private:
inline void scanChar();
int scanToken();
int scanNumber(QChar ch);
+ enum ScanStringMode {
+ SingleQuote = '\'',
+ DoubleQuote = '"',
+ TemplateHead = '`',
+ TemplateContinuation = 0
+ };
+ int scanString(ScanStringMode mode);
bool isLineTerminator() const;
unsigned isLineTerminatorSequence() const;
@@ -202,6 +210,10 @@ private:
ParenthesesState _parenthesesState;
int _parenthesesCount;
+ // template string stack
+ QStack<int> _outerTemplateBraceCount;
+ int _bracesCount = -1;
+
int _stackToken;
int _patternFlags;
diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
index ba2b836a6d..2722a536b6 100644
--- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
+++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
@@ -50,6 +50,9 @@ private slots:
void qmlParser();
#endif
void invalidEscapeSequence();
+ void stringLiteral();
+ void noSubstitutionTemplateLiteral();
+ void templateLiteral();
private:
QStringList excludedDirs;
@@ -204,6 +207,67 @@ void tst_qqmlparser::invalidEscapeSequence()
parser.parse();
}
+void tst_qqmlparser::stringLiteral()
+{
+ using namespace QQmlJS;
+
+ Engine engine;
+ Lexer lexer(&engine);
+ QLatin1String code("'hello string'");
+ lexer.setCode(code , 1);
+ Parser parser(&engine);
+ QVERIFY(parser.parseExpression());
+ AST::ExpressionNode *expression = parser.expression();
+ QVERIFY(expression);
+ auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression);
+ QVERIFY(literal);
+ QCOMPARE(literal->value, "hello string");
+ QCOMPARE(literal->firstSourceLocation().begin(), 0);
+ QCOMPARE(literal->lastSourceLocation().end(), code.size());
+}
+
+void tst_qqmlparser::noSubstitutionTemplateLiteral()
+{
+ using namespace QQmlJS;
+
+ Engine engine;
+ Lexer lexer(&engine);
+ QLatin1String code("`hello template`");
+ lexer.setCode(code, 1);
+ Parser parser(&engine);
+ QVERIFY(parser.parseExpression());
+ AST::ExpressionNode *expression = parser.expression();
+ QVERIFY(expression);
+
+ auto *literal = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression);
+ QVERIFY(literal);
+
+ QCOMPARE(literal->value, "hello template");
+ QCOMPARE(literal->firstSourceLocation().begin(), 0);
+ QCOMPARE(literal->lastSourceLocation().end(), code.size());
+}
+
+void tst_qqmlparser::templateLiteral()
+{
+ using namespace QQmlJS;
+
+ Engine engine;
+ Lexer lexer(&engine);
+ QLatin1String code("`one plus one equals ${1+1}!`");
+ lexer.setCode(code, 1);
+ Parser parser(&engine);
+ QVERIFY(parser.parseExpression());
+ AST::ExpressionNode *expression = parser.expression();
+ QVERIFY(expression);
+
+ auto *templateLiteral = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression);
+ QVERIFY(templateLiteral);
+
+ QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0);
+ auto *e = templateLiteral->expression;
+ QVERIFY(e);
+}
+
QTEST_MAIN(tst_qqmlparser)
#include "tst_qqmlparser.moc"