/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**/
#include "qqmljsastdumper.h"
#include <private/qqmljsast_p.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtCore/QTextStream>

QT_BEGIN_NAMESPACE

namespace QQmlJS {
using namespace AST;
/*!
\internal
\enum QQmlJS::DumperOptions

This enum type specifies the options for the AstDumper.
The values can be combined with the '|' operator, and checked using the '&' operator.

\value None
       Default dumping options
\value NoLocations
       Does not dump SourceLocations, allowing one to compare equivalent AST
       generated by code formatted differently
\value NoAnnotations
       Does not dump annotations
\value DumpNode
       Does dump a <Node></Node> in preVisit/postVisit
*/

/*!
\internal
\class QQmlJS::AstDumper
\brief Dumps or compares AST in an xml like format, mostly for testing/debugging

Initialize it with a lambda that dumps a string, and configure it with .setX methods.
If \l{indent} is set to a non zero value the xml is indented by that amount, and
\l{baseIndent} is the initial indent.
If \l{emitNode} is true the node tag is emitted in the preVisit/postVisit.
If \l{emitLocation} is true the SourceLocations are emitted.
If \l{emitAnnotations} is true annotations are emitted

The implementation has unnecessary roundtrips to QString, but it is supposed to be used
for debugging purposes...

Probably you will not use the visitor at all but rather the static method diff or
the qDebug() and ostream operator << that use the visitor...

\fn AstDumper::diff(AST::Node *n1, AST::Node *n2, int nContext, DumperOptions opt, int indent)

\brief compares the two AST::Node n1 and n2 and returns a string describing their first difference

If there are no differences the empty string is returned, so .isEmpty() can be use to check
for no differences.
\l{nContext} decides how much context is printed out.

*/


QDebug operator<<(QDebug d, AST::Node *n) {
    QDebug noQuote = d.noquote().nospace();
    AstDumper visitor([&noQuote](const QString &s){ noQuote << s; });
    Node::accept(n, &visitor);
    return d;
}


std::ostream &operator<<(std::ostream &stream, AST::Node *n) {
    AstDumper visitor([&stream](const QString &s){ stream << s.toStdString(); });
    Node::accept(n, &visitor);
    return stream;
}

bool operator & (DumperOptions lhs, DumperOptions rhs) {
    return bool(static_cast<int>(lhs) & static_cast<int>(rhs));
}

DumperOptions operator | (DumperOptions lhs, DumperOptions rhs) {
    return DumperOptions(static_cast<int>(lhs) | static_cast<int>(rhs));
}

QString AstDumper::diff(AST::Node *n1, AST::Node *n2, int nContext, DumperOptions opt, int indent) {
    QString s1, s2;
    QTextStream d1(&s1), d2(&s2);
    AstDumper visitor1=AstDumper([&d1](const QString &s){ d1 << s; }, opt, indent);
    AstDumper visitor2=AstDumper([&d2](const QString &s){ d2 << s; }, opt, indent);
    Node::accept(n1, &visitor1);
    Node::accept(n2, &visitor2);
    d1.seek(0);
    d2.seek(0);
    std::vector<QString> preLines(nContext);
    int nLine = 0;
    bool same = true;
    QString l1, l2;
    while (same && !d1.atEnd() && !d2.atEnd()) {
        l1=d1.readLine();
        l2=d2.readLine();
        if (l1 == l2)
            preLines[nLine++ % nContext] = l1;
        else
            same = false;
    }
    QString res;
    QTextStream ss(&res);
    if (!same || !d1.atEnd() || !d2.atEnd()) {
        for (int iline = qMin(nLine, nContext); iline > 0; --iline) {
            ss << QLatin1String(" ") << preLines[(nLine - iline) % nContext] << QLatin1String("\n");
        }
        int iline = 0;
        if (!same) {
            ss << QLatin1String("-") << l1 << QLatin1String("\n");
            ++iline;
        }
        if (same && nContext == 0)
            nContext = 1;
        for (;iline < nContext && !d1.atEnd(); iline ++) {
            l1 = d1.readLine();
            ss << QLatin1String("-") << l1 << QLatin1String("\n");
        }
        iline = 0;
        if (!same) {
            ss << QLatin1String("+") << l2 << QLatin1String("\n");
            ++iline;
        }
        for (;iline < nContext && !d2.atEnd(); iline ++) {
            l2 = d2.readLine();
            ss << QLatin1String("+") << l2 << QLatin1String("\n");
        }
    }
    return res;
}

QString AstDumper::printNode(Node *n, DumperOptions opt, int indent, int baseIndent)
{
    QString res;
    QTextStream d(&res);
    AstDumper visitor=AstDumper([&d](const QString &s){ d << s; }, opt, indent, baseIndent);
    Node::accept(n, &visitor);
    return res;
}

AstDumper::AstDumper(const std::function<void(const QString &)> &dumper, DumperOptions options, int indent, int baseIndent):
    dumper(dumper), options(options), indent(indent), baseIndent(baseIndent) {}

void AstDumper::start(const QString &str) {
    dumper(QString::fromLatin1(" ").repeated(baseIndent));
    dumper(QLatin1String("<"));
    dumper(str);
    dumper(QLatin1String(">\n"));
    baseIndent += indent;
}

void AstDumper::start(const char *str) {
    start(QLatin1String(str));
}

void AstDumper::stop(const QString &str) {
    baseIndent -= indent;
    dumper(QString::fromLatin1(" ").repeated(baseIndent));
    dumper(QLatin1String("</"));
    dumper(str);
    dumper(QLatin1String(">\n"));
}

void AstDumper::stop(const char *str) {
    stop(QLatin1String(str));
}

QString AstDumper::qs(const QString &s) {
    QString res(s);
    return QLatin1String("\"") + res
            .replace(QLatin1String("\\"), QLatin1String("\\\\"))
            .replace(QLatin1String("\""), QLatin1String("\\\"")) + QLatin1String("\"");
}

QString AstDumper::qs(const char *s) {
    return qs(QLatin1String(s));
}

QString AstDumper::qs(const QStringRef &s) {
    return qs(s.toString());
}

QString AstDumper::loc(const SourceLocation &s) {
    if (noLocations() || !s.isValid())
        return QLatin1String("\"\"");
    else {
        return QLatin1String("\"off:%1 len:%2 l:%3 c:%4\"").arg(QString::number(s.offset), QString::number(s.length), QString::number(s.startLine), QString::number(s.startColumn));
    }
}

QString AstDumper::boolStr(bool v) { return (v ? qs("true"): qs("false")); }

bool AstDumper::preVisit(Node *) { if (dumpNode()) start("Node"); return true; }

void AstDumper::postVisit(Node *) { if (dumpNode()) stop("Node"); }

bool AstDumper::visit(UiProgram *) { start("UiProgram"); return true; }

bool AstDumper::visit(UiHeaderItemList *) { start("UiHeaderItemList"); return true; }

bool AstDumper::visit(UiPragma *el) {
    start(QLatin1String("UiPragma name=%1 pragmaToken=%2 semicolonToken=%3")
          .arg(qs(el->name), loc(el->pragmaToken), loc(el->semicolonToken)));
    return true;
}

bool AstDumper::visit(UiImport *el)
{
    start(QLatin1String("UiImport fileName=%1 importId=%2 importToken=%3 fileNameToken=%4 asToken=%5 importIdToken=%6 semicolonToken=%7")
          .arg(qs(el->fileName), qs(el->importId), loc(el->importToken), loc(el->fileNameToken), loc(el->asToken), loc(el->importIdToken), loc(el->semicolonToken)));
    return true;
}

bool AstDumper::visit(UiPublicMember *el) {
    QString typeStr = ((el->type == UiPublicMember::Signal)   ? QLatin1String("Signal") :
                                                                (el->type == UiPublicMember::Property) ? QLatin1String("Property") : QLatin1String("Unexpected(%1)").arg(el->type));
    start(QLatin1String("UiPublicMember type=%1 typeModifier=%2 name=%3 isDefaultMember=%4 isReadonlyMember=%5 isRequired=%6 "
                        "defaultToken=%7 readonlyToken=%8 propertyToken=%9 requiredToken=%10 typeModifierToken=%11 typeToken=%12 "
                        "identifierToken=%13 colonToken=%14 semicolonToken=%15")
          .arg(qs(typeStr), qs(el->typeModifier), qs(el->name),
               el->isDefaultMember, el->isReadonlyMember, el->isRequired,
               loc(el->defaultToken), loc(el->readonlyToken), loc(el->propertyToken),
               loc(el->requiredToken), loc(el->typeModifierToken), loc(el->typeToken),
               loc(el->identifierToken), loc(el->colonToken), loc(el->semicolonToken)
               ));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    Node::accept(el->memberType, this);
    return true;
}

bool AstDumper::visit(AST::UiSourceElement *el) {
    start(QLatin1String("UiSourceElement"));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiObjectDefinition *el) {
    start("UiObjectDefinition");
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiObjectInitializer *el) {
    start(QLatin1String("UiObjectInitializer lbraceToken=%1 rbraceToken=%2")
          .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
    return true;
}

bool AstDumper::visit(AST::UiObjectBinding *el) {
    start(QLatin1String("UiObjectBinding colonToken=%1 hasOnToken=%2")
          .arg(loc(el->colonToken), boolStr(el->hasOnToken)));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiScriptBinding *el) {
    start(QLatin1String("UiScriptBinding colonToken=%1")
          .arg(loc(el->colonToken)));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiArrayBinding *el) {
    start(QLatin1String("UiArrayBinding colonToken=%1 lbracketToken=%2 rbracketToken=%3")
          .arg(loc(el->colonToken), loc(el->lbracketToken), loc(el->rbracketToken)));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiParameterList *el) {
    start(QLatin1String("UiArrayBinding name=%1 commaToken=%2 propertyTypeToken=%3 identifierToken=%4 colonToken=%5")
          .arg(qs(el->name), loc(el->commaToken), loc(el->propertyTypeToken), loc(el->identifierToken), loc(el->colonToken)));
    Node::accept(el->type, this);
    return true;
}

bool AstDumper::visit(AST::UiObjectMemberList *) { start("UiObjectMemberList"); return true; }

bool AstDumper::visit(AST::UiArrayMemberList *el) {
    start(QLatin1String("UiArrayMemberList commaToken=%1")
          .arg(loc(el->commaToken)));
    return true;
}

bool AstDumper::visit(AST::UiQualifiedId *el) {
    start(QLatin1String("UiQualifiedId name=%1 identifierToken=%2")
          .arg(qs(el->name), loc(el->identifierToken)));
    Node::accept(el->next, this);
    return true;
}

bool AstDumper::visit(AST::UiEnumDeclaration *el) {
    start(QLatin1String("UiEnumDeclaration enumToken=%1 rbraceToken=%2 name=%3")
          .arg(loc(el->enumToken), loc(el->rbraceToken), qs(el->name)));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(AST::UiEnumMemberList *el) {
    start(QLatin1String("UiEnumMemberList member=%1 value=%2 memberToken=%3 valueToken=%4")
          .arg(qs(el->member), qs(QString::number(el->value)), loc(el->memberToken), loc(el->valueToken)));
    return true;
}

bool AstDumper::visit(AST::UiVersionSpecifier *el) {
    start(QLatin1String("UiVersionSpecifier majorVersion=%1 minorVersion=%2 majorToken=%3 minorToken=%4")
          .arg(qs(QString::number(el->majorVersion)), qs(QString::number(el->minorVersion)), loc(el->majorToken), loc(el->minorToken)));
    return true;
}

bool AstDumper::visit(AST::UiInlineComponent *el) {
    start(QLatin1String("UiInlineComponent name=%1 componentToken=%2")
          .arg(qs(el->name), loc(el->componentToken)));
    if (!noAnnotations()) // put annotations inside the node they refer to
        Node::accept(el->annotations, this);
    return true;
}

bool AstDumper::visit(UiRequired *el)
{
    start(QLatin1String("UiRequired name=%1 requiredToken=%2 semicolonToken=%3")
          .arg(qs(el->name), loc(el->requiredToken), loc(el->semicolonToken)));
    return true;
}

bool AstDumper::visit(UiAnnotation *)
{
    start(QLatin1String("UiAnnotation"));
    return true;
}

bool AstDumper::visit(UiAnnotationList *)
{
    start(QLatin1String("UiAnnotationList"));
    return true;
}

void AstDumper::endVisit(AST::UiProgram *) { stop("UiProgram"); }

void AstDumper::endVisit(AST::UiImport *el) {
    Node::accept(el->version, this);
    stop("UiImport");
}

void AstDumper::endVisit(AST::UiHeaderItemList *) { stop("UiHeaderItemList"); }

void AstDumper::endVisit(AST::UiPragma *) { stop("UiPragma"); }

void AstDumper::endVisit(AST::UiPublicMember *el) {
    Node::accept(el->parameters, this);
    stop("UiPublicMember");
}

void AstDumper::endVisit(AST::UiSourceElement *) { stop("UiSourceElement"); }
void AstDumper::endVisit(AST::UiObjectDefinition *) { stop("UiObjectDefinition"); }
void AstDumper::endVisit(AST::UiObjectInitializer *) { stop("UiObjectInitializer"); }
void AstDumper::endVisit(AST::UiObjectBinding *) { stop("UiObjectBinding"); }
void AstDumper::endVisit(AST::UiScriptBinding *) { stop("UiScriptBinding"); }
void AstDumper::endVisit(AST::UiArrayBinding *) { stop("UiArrayBinding"); }
void AstDumper::endVisit(AST::UiParameterList *el) {
    stop("UiParameterList");
    Node::accept(el->next, this); // put other args at the same level as this one...
}
void AstDumper::endVisit(AST::UiObjectMemberList *) { stop("UiObjectMemberList"); }
void AstDumper::endVisit(AST::UiArrayMemberList *) { stop("UiArrayMemberList"); }
void AstDumper::endVisit(AST::UiQualifiedId *) { stop("UiQualifiedId"); }
void AstDumper::endVisit(AST::UiEnumDeclaration *) { stop("UiEnumDeclaration"); }
void AstDumper::endVisit(AST::UiEnumMemberList *el) {
    stop("UiEnumMemberList");
    Node::accept(el->next, this); // put other enum members at the same level as this one...
}
void AstDumper::endVisit(AST::UiVersionSpecifier *) { stop("UiVersionSpecifier"); }
void AstDumper::endVisit(AST::UiInlineComponent *) { stop("UiInlineComponent"); }
void AstDumper::endVisit(UiRequired *) { stop("UiRequired"); }
void AstDumper::endVisit(UiAnnotation *) { stop("UiAnnotation"); }
void AstDumper::endVisit(UiAnnotationList *) { stop("UiAnnotationList"); }

// QQmlJS
bool AstDumper::visit(AST::ThisExpression *el) {
    start(QLatin1String("ThisExpression thisToken=%1")
          .arg(loc(el->thisToken)));
    return true;
}
void AstDumper::endVisit(AST::ThisExpression *) { stop("ThisExpression"); }

bool AstDumper::visit(AST::IdentifierExpression *el) {
    start(QLatin1String("IdentifierExpression name=%1 identiferToken=%2")
          .arg(qs(el->name), loc(el->identifierToken)));
    return true;
}
void AstDumper::endVisit(AST::IdentifierExpression *) { stop("IdentifierExpression"); }

bool AstDumper::visit(AST::NullExpression *el) {
    start(QLatin1String("NullExpression nullToken=%1")
          .arg(loc(el->nullToken)));
    return true;
}
void AstDumper::endVisit(AST::NullExpression *) { stop("NullExpression"); }

bool AstDumper::visit(AST::TrueLiteral *el) {
    start(QLatin1String("TrueLiteral trueToken=%1")
          .arg(loc(el->trueToken)));
    return true;
}
void AstDumper::endVisit(AST::TrueLiteral *) { stop("TrueLiteral"); }

bool AstDumper::visit(AST::FalseLiteral *el) {
    start(QLatin1String("FalseLiteral falseToken=%1")
          .arg(loc(el->falseToken)));
    return true;
}
void AstDumper::endVisit(AST::FalseLiteral *) { stop("FalseLiteral"); }

bool AstDumper::visit(AST::SuperLiteral *el) {
    start(QLatin1String("SuperLiteral superToken=%1")
          .arg(loc(el->superToken)));
    return true;
}
void AstDumper::endVisit(AST::SuperLiteral *) { stop("SuperLiteral"); }

bool AstDumper::visit(AST::StringLiteral *el) {
    start(QLatin1String("StringLiteral value=%1 literalToken=%2")
          .arg(qs(el->value), loc(el->literalToken)));
    return true;
}
void AstDumper::endVisit(AST::StringLiteral *) { stop("StringLiteral"); }

bool AstDumper::visit(AST::TemplateLiteral *el) {
    start(QLatin1String("TemplateLiteral value=%1 rawValue=%2 literalToken=%3")
          .arg(qs(el->value), qs(el->rawValue), loc(el->literalToken)));
    Node::accept(el->expression, this);
    return true;
}
void AstDumper::endVisit(AST::TemplateLiteral *) { stop("TemplateLiteral"); }

bool AstDumper::visit(AST::NumericLiteral *el) {
    start(QLatin1String("NumericLiteral value=%1 literalToken=%2")
          .arg(qs(QString::number(el->value)), loc(el->literalToken)));
    return true;
}
void AstDumper::endVisit(AST::NumericLiteral *) { stop("NumericLiteral"); }

bool AstDumper::visit(AST::RegExpLiteral *el) {
    start(QLatin1String("RegExpLiteral pattern=%1 flags=%2 literalToken=%3")
          .arg(qs(el->pattern), qs(QString::number(el->flags, 16)), loc(el->literalToken)));
    return true;
}
void AstDumper::endVisit(AST::RegExpLiteral *) { stop("RegExpLiteral"); }

bool AstDumper::visit(AST::ArrayPattern *el) {
    start(QLatin1String("ArrayPattern lbracketToken=%1, commaToken=%2, rbracketToken=%3 parseMode=%4")
          .arg(loc(el->lbracketToken),loc(el->commaToken),loc(el->rbracketToken), qs(QString::number(el->parseMode, 16))));
    return true;
}
void AstDumper::endVisit(AST::ArrayPattern *) { stop("ArrayPattern"); }

bool AstDumper::visit(AST::ObjectPattern *el) {
    start(QLatin1String("ObjectPattern lbraceToken=%1 rbraceToken=%2 parseMode=%3")
          .arg(loc(el->lbraceToken), loc(el->rbraceToken), qs(QString::number(el->parseMode, 16))));
    return true;
}
void AstDumper::endVisit(AST::ObjectPattern *) { stop("ObjectPattern"); }

bool AstDumper::visit(AST::PatternElementList *) { start("PatternElementList"); return true; }
void AstDumper::endVisit(AST::PatternElementList *) { stop("PatternElementList"); }

bool AstDumper::visit(AST::PatternPropertyList *) { start("PatternPropertyList"); return true; }
void AstDumper::endVisit(AST::PatternPropertyList *) { stop("PatternPropertyList"); }

bool AstDumper::visit(AST::PatternElement *el) {
    start(QLatin1String("PatternElement identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5")
          .arg(loc(el->identifierToken), qs(el->bindingIdentifier), qs(QString::number(el->type, 16)),
               qs(QString::number(static_cast<int>(el->scope), 16)), boolStr(el->isForDeclaration)));
    return true;
}
void AstDumper::endVisit(AST::PatternElement *) { stop("PatternElement"); }

bool AstDumper::visit(AST::PatternProperty *el) {
    start(QLatin1String("PatternProperty identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5 colonToken=%6")
          .arg(loc(el->identifierToken), qs(el->bindingIdentifier), qs(QString::number(el->type, 16)),
               qs(QString::number(static_cast<int>(el->scope), 16)), boolStr(el->isForDeclaration), loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::PatternProperty *) { stop("PatternProperty"); }

bool AstDumper::visit(AST::Elision *el) {
    start(QLatin1String("Elision commaToken=%1")
          .arg(loc(el->commaToken)));
    return true;
}
void AstDumper::endVisit(AST::Elision *el) {
    stop("Elision");
    Node::accept(el->next, this); // emit other elisions at the same level
}

bool AstDumper::visit(AST::NestedExpression *el) {
    start(QLatin1String("NestedExpression lparenToken=%1 rparenToken=%2")
          .arg(loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::NestedExpression *) { stop("NestedExpression"); }

bool AstDumper::visit(AST::IdentifierPropertyName *el) {
    start(QLatin1String("IdentifierPropertyName id=%1 propertyNameToken=%2")
          .arg(qs(el->id), loc(el->propertyNameToken)));
    return true;
}
void AstDumper::endVisit(AST::IdentifierPropertyName *) { stop("IdentifierPropertyName"); }

bool AstDumper::visit(AST::StringLiteralPropertyName *el) {
    start(QLatin1String("StringLiteralPropertyName id=%1 propertyNameToken=%2")
          .arg(qs(el->id), loc(el->propertyNameToken)));
    return true;
}
void AstDumper::endVisit(AST::StringLiteralPropertyName *) { stop("StringLiteralPropertyName"); }

bool AstDumper::visit(AST::NumericLiteralPropertyName *el) {
    start(QLatin1String("NumericLiteralPropertyName id=%1 propertyNameToken=%2")
          .arg(qs(QString::number(el->id)),loc(el->propertyNameToken)));
    return true;
}
void AstDumper::endVisit(AST::NumericLiteralPropertyName *) { stop("NumericLiteralPropertyName"); }

bool AstDumper::visit(AST::ComputedPropertyName *) {
    start(QLatin1String("ComputedPropertyName"));
    return true;
}
void AstDumper::endVisit(AST::ComputedPropertyName *) { stop("ComputedPropertyName"); }

bool AstDumper::visit(AST::ArrayMemberExpression *el) {
    start(QLatin1String("ArrayMemberExpression lbraketToken=%1 rbraketToken=%2")
          .arg(loc(el->lbracketToken), loc(el->rbracketToken)));
    return true;
}
void AstDumper::endVisit(AST::ArrayMemberExpression *) { stop("ArrayMemberExpression"); }

bool AstDumper::visit(AST::FieldMemberExpression *el) {
    start(QLatin1String("FieldMemberExpression name=%1 dotToken=%2 identifierToken=%3")
          .arg(qs(el->name), loc(el->dotToken), loc(el->identifierToken)));
    return true;
}
void AstDumper::endVisit(AST::FieldMemberExpression *) { stop("FieldMemberExpression"); }

bool AstDumper::visit(AST::TaggedTemplate *) {
    start(QLatin1String("TaggedTemplate"));
    return true;
}
void AstDumper::endVisit(AST::TaggedTemplate *) { stop("TaggedTemplate"); }

bool AstDumper::visit(AST::NewMemberExpression *el) {
    start(QLatin1String("NewMemberExpression newToken=%1 lparenToken=%2 rparenToken=%3")
          .arg(loc(el->newToken), loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::NewMemberExpression *) { stop("NewMemberExpression"); }

bool AstDumper::visit(AST::NewExpression *el) {
    start(QLatin1String("NewExpression newToken=%1")
          .arg(loc(el->newToken)));
    return true;
}
void AstDumper::endVisit(AST::NewExpression *) { stop("NewExpression"); }

bool AstDumper::visit(AST::CallExpression *el) {
    start(QLatin1String("CallExpression lparenToken=%1 rparenToken=%2")
          .arg(loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::CallExpression *) { stop("CallExpression"); }

bool AstDumper::visit(AST::ArgumentList *el) {
    start(QLatin1String("ArgumentList commaToken=%1 isSpreadElement=%2")
          .arg(loc(el->commaToken), boolStr(el->isSpreadElement)));
    return true;
}
void AstDumper::endVisit(AST::ArgumentList *) { stop("ArgumentList"); }

bool AstDumper::visit(AST::PostIncrementExpression *el) {
    start(QLatin1String("PostIncrementExpression incrementToken=%1")
          .arg(loc(el->incrementToken)));
    return true;
}
void AstDumper::endVisit(AST::PostIncrementExpression *) { stop("PostIncrementExpression"); }

bool AstDumper::visit(AST::PostDecrementExpression *el) {
    start(QLatin1String("PostDecrementExpression decrementToken=%1")
          .arg(loc(el->decrementToken)));
    return true;
}
void AstDumper::endVisit(AST::PostDecrementExpression *) { stop("PostDecrementExpression"); }

bool AstDumper::visit(AST::DeleteExpression *el) {
    start(QLatin1String("DeleteExpression deleteToken=%1")
          .arg(loc(el->deleteToken)));
    return true;
}
void AstDumper::endVisit(AST::DeleteExpression *) { stop("DeleteExpression"); }

bool AstDumper::visit(AST::VoidExpression *el) {
    start(QLatin1String("VoidExpression voidToken=%1")
          .arg(loc(el->voidToken)));
    return true;
}
void AstDumper::endVisit(AST::VoidExpression *) { stop("VoidExpression"); }

bool AstDumper::visit(AST::TypeOfExpression *el) {
    start(QLatin1String("TypeOfExpression typeofToken=%1")
          .arg(loc(el->typeofToken)));
    return true;
}
void AstDumper::endVisit(AST::TypeOfExpression *) { stop("TypeOfExpression"); }

bool AstDumper::visit(AST::PreIncrementExpression *el) {
    start(QLatin1String("PreIncrementExpression incrementToken=%1")
          .arg(loc(el->incrementToken)));
    return true;
}
void AstDumper::endVisit(AST::PreIncrementExpression *) { stop("PreIncrementExpression"); }

bool AstDumper::visit(AST::PreDecrementExpression *el) {
    start(QLatin1String("PreDecrementExpression decrementToken=%1")
          .arg(loc(el->decrementToken)));
    return true;
}
void AstDumper::endVisit(AST::PreDecrementExpression *) { stop("PreDecrementExpression"); }

bool AstDumper::visit(AST::UnaryPlusExpression *el) {
    start(QLatin1String("UnaryPlusExpression plusToken=%1")
          .arg(loc(el->plusToken)));
    return true;
}
void AstDumper::endVisit(AST::UnaryPlusExpression *) { stop("UnaryPlusExpression"); }

bool AstDumper::visit(AST::UnaryMinusExpression *el) {
    start(QLatin1String("UnaryMinusExpression minusToken=%1")
          .arg(loc(el->minusToken)));
    return true;
}
void AstDumper::endVisit(AST::UnaryMinusExpression *) { stop("UnaryMinusExpression"); }

bool AstDumper::visit(AST::TildeExpression *el) {
    start(QLatin1String("TildeExpression tildeToken=%1")
          .arg(loc(el->tildeToken)));
    return true;
}
void AstDumper::endVisit(AST::TildeExpression *) { stop("TildeExpression"); }

bool AstDumper::visit(AST::NotExpression *el) {
    start(QLatin1String("NotExpression notToken=%1")
          .arg(loc(el->notToken)));
    return true;
}
void AstDumper::endVisit(AST::NotExpression *) { stop("NotExpression"); }

bool AstDumper::visit(AST::BinaryExpression *el) {
    start(QLatin1String("BinaryExpression op=%1 operatorToken=%2")
          .arg(qs(QString::number(el->op,16)), loc(el->operatorToken)));
    return true;
}
void AstDumper::endVisit(AST::BinaryExpression *) { stop("BinaryExpression"); }

bool AstDumper::visit(AST::ConditionalExpression *el) {
    start(QLatin1String("ConditionalExpression questionToken=%1 colonToken=%2")
          .arg(loc(el->questionToken), loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::ConditionalExpression *) { stop("ConditionalExpression"); }

bool AstDumper::visit(AST::Expression *el) {
    start(QLatin1String("Expression commaToken=%1")
          .arg(loc(el->commaToken)));
    return true;
}
void AstDumper::endVisit(AST::Expression *) { stop("Expression"); }

bool AstDumper::visit(AST::Block *el) {
    start(QLatin1String("Block lbraceToken=%1 rbraceToken=%2")
          .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::Block *) { stop("Block"); }

bool AstDumper::visit(AST::StatementList *) {
    start(QLatin1String("StatementList"));
    return true;
}
void AstDumper::endVisit(AST::StatementList *) { stop("StatementList"); }

bool AstDumper::visit(AST::VariableStatement *el) {
    start(QLatin1String("VariableStatement declarationKindToken=%1")
          .arg(loc(el->declarationKindToken)));
    return true;
}
void AstDumper::endVisit(AST::VariableStatement *) { stop("VariableStatement"); }

bool AstDumper::visit(AST::VariableDeclarationList *el) {
    start(QLatin1String("VariableDeclarationList commaToken=%1")
          .arg(loc(el->commaToken)));
    return true;
}
void AstDumper::endVisit(AST::VariableDeclarationList *) { stop("VariableDeclarationList"); }

bool AstDumper::visit(AST::EmptyStatement *el) {
    start(QLatin1String("EmptyStatement semicolonToken=%1")
          .arg(loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::EmptyStatement *) { stop("EmptyStatement"); }

bool AstDumper::visit(AST::ExpressionStatement *el) {
    start(QLatin1String("ExpressionStatement semicolonToken=%1")
          .arg(loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::ExpressionStatement *) { stop("ExpressionStatement"); }

bool AstDumper::visit(AST::IfStatement *el) {
    start(QLatin1String("IfStatement ifToken=%1 lparenToken=%2 rparenToken=%3 elseToken=%4")
          .arg(loc(el->ifToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->elseToken)));
    return true;
}
void AstDumper::endVisit(AST::IfStatement *) { stop("IfStatement"); }

bool AstDumper::visit(AST::DoWhileStatement *el) {
    start(QLatin1String("DoWhileStatement doToken=%1 whileToken=%2 lparenToken=%3 rparenToken=%4 semicolonToken=%5")
          .arg(loc(el->doToken), loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::DoWhileStatement *) { stop("DoWhileStatement"); }

bool AstDumper::visit(AST::WhileStatement *el) {
    start(QLatin1String("WhileStatement whileToken=%1 lparenToken=%2 rparenToken=%3")
          .arg(loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::WhileStatement *) { stop("WhileStatement"); }

bool AstDumper::visit(AST::ForStatement *el) {
    start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 firstSemicolonToken=%3 secondSemicolonToken=%4 rparenToken=%5")
          .arg(loc(el->forToken), loc(el->lparenToken), loc(el->firstSemicolonToken), loc(el->secondSemicolonToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::ForStatement *) { stop("ForStatement"); }

bool AstDumper::visit(AST::ForEachStatement *el) {
    start(QLatin1String("ForEachStatement forToken=%1 lparenToken=%2 inOfToken=%3 rparenToken=%4 type=%5")
          .arg(loc(el->forToken), loc(el->lparenToken), loc(el->inOfToken), loc(el->rparenToken), qs(QString::number(static_cast<int>(el->type), 16))));
    return true;
}
void AstDumper::endVisit(AST::ForEachStatement *) { stop("ForEachStatement"); }

bool AstDumper::visit(AST::ContinueStatement *el) {
    start(QLatin1String("ContinueStatement label=%1 continueToken=%2 identifierToken=%3 semicolonToken=%4")
          .arg(qs(el->label), loc(el->continueToken), loc(el->identifierToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::ContinueStatement *) { stop("ContinueStatement"); }

bool AstDumper::visit(AST::BreakStatement *el) {
    start(QLatin1String("BreakStatement label=%1 breakToken=%2 identifierToken=%3 semicolonToken=%4")
          .arg(qs(el->label), loc(el->breakToken), loc(el->identifierToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::BreakStatement *) { stop("BreakStatement"); }

bool AstDumper::visit(AST::ReturnStatement *el) {
    start(QLatin1String("ReturnStatement returnToken=%1 semicolonToken=%2")
          .arg(loc(el->returnToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::ReturnStatement *) { stop("ReturnStatement"); }

bool AstDumper::visit(AST::YieldExpression *el) {
    start(QLatin1String("YieldExpression isYieldStar=%1 yieldToken=%2")
          .arg(boolStr(el->isYieldStar), loc(el->yieldToken)));
    return true;
}
void AstDumper::endVisit(AST::YieldExpression *) { stop("YieldExpression"); }

bool AstDumper::visit(AST::WithStatement *el) {
    start(QLatin1String("WithStatement withToken=%1 lparenToken=%2 rparenToken=%3")
          .arg(loc(el->withToken), loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::WithStatement *) { stop("WithStatement"); }

bool AstDumper::visit(AST::SwitchStatement *el) {
    start(QLatin1String("SwitchStatement switchToken=%1 lparenToken=%2 rparenToken=%3")
          .arg(loc(el->switchToken), loc(el->lparenToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::SwitchStatement *) { stop("SwitchStatement"); }

bool AstDumper::visit(AST::CaseBlock *el) {
    start(QLatin1String("CaseBlock lbraceToken=%1 rbraceToken=%2")
          .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::CaseBlock *) { stop("CaseBlock"); }

bool AstDumper::visit(AST::CaseClauses *) {
    start(QLatin1String("CaseClauses"));
    return true;
}
void AstDumper::endVisit(AST::CaseClauses *) { stop("CaseClauses"); }

bool AstDumper::visit(AST::CaseClause *el) {
    start(QLatin1String("CaseClause caseToken=%1 colonToken=%2")
          .arg(loc(el->caseToken), loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::CaseClause *) { stop("CaseClause"); }

bool AstDumper::visit(AST::DefaultClause *el) {
    start(QLatin1String("DefaultClause defaultToken=%1 colonToken=%2")
          .arg(loc(el->defaultToken), loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::DefaultClause *) { stop("DefaultClause"); }

bool AstDumper::visit(AST::LabelledStatement *el) {
    start(QLatin1String("LabelledStatement label=%1 identifierToken=%2 colonToken=%3")
          .arg(qs(el->label), loc(el->identifierToken), loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::LabelledStatement *) { stop("LabelledStatement"); }

bool AstDumper::visit(AST::ThrowStatement *el) {
    start(QLatin1String("ThrowStatement throwToken=%1 semicolonToken=%2")
          .arg(loc(el->throwToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::ThrowStatement *) { stop("ThrowStatement"); }

bool AstDumper::visit(AST::TryStatement *el) {
    start(QLatin1String("TryStatement tryToken=%1")
          .arg(loc(el->tryToken)));
    return true;
}
void AstDumper::endVisit(AST::TryStatement *) { stop("TryStatement"); }

bool AstDumper::visit(AST::Catch *el) {
    start(QLatin1String("Catch catchToken=%1 lparenToken=%2 identifierToken=%3 rparenToken=%4")
          .arg(loc(el->catchToken), loc(el->lparenToken), loc(el->identifierToken), loc(el->rparenToken)));
    return true;
}
void AstDumper::endVisit(AST::Catch *) { stop("Catch"); }

bool AstDumper::visit(AST::Finally *el) {
    start(QLatin1String("Finally finallyToken=%1")
          .arg(loc(el->finallyToken)));
    return true;
}
void AstDumper::endVisit(AST::Finally *) { stop("Finally"); }

bool AstDumper::visit(AST::FunctionDeclaration *el) {
    start(QLatin1String("FunctionDeclaration name=%1 isArrowFunction=%2 isGenerator=%3 functionToken=%4 "
                        "identifierToken=%5 lparenToken=%6 rparenToken=%7 lbraceToken=%8 rbraceToken=%9")
          .arg(qs(el->name), boolStr(el->isArrowFunction), boolStr(el->isGenerator), loc(el->functionToken),
               loc(el->identifierToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->lbraceToken),
               loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::FunctionDeclaration *) { stop("FunctionDeclaration"); }

bool AstDumper::visit(AST::FunctionExpression *el) {
    start(QLatin1String("FunctionExpression name=%1 isArrowFunction=%2 isGenerator=%3 functionToken=%4 "
                        "identifierToken=%5 lparenToken=%6 rparenToken=%7 lbraceToken=%8 rbraceToken=%9")
          .arg(qs(el->name), boolStr(el->isArrowFunction), boolStr(el->isGenerator), loc(el->functionToken),
               loc(el->identifierToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->lbraceToken),
               loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::FunctionExpression *) { stop("FunctionExpression"); }

bool AstDumper::visit(AST::FormalParameterList *) {
    start(QLatin1String("FormalParameterList"));
    return true;
}
void AstDumper::endVisit(AST::FormalParameterList *) { stop("FormalParameterList"); }

bool AstDumper::visit(AST::ClassExpression *el) {
    start(QLatin1String("ClassExpression name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5")
          .arg(qs(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::ClassExpression *) { stop("ClassExpression"); }

bool AstDumper::visit(AST::ClassDeclaration *el) {
    start(QLatin1String("ClassDeclaration name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5")
          .arg(qs(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken)));
    return true;
}
void AstDumper::endVisit(AST::ClassDeclaration *) { stop("ClassDeclaration"); }

bool AstDumper::visit(AST::ClassElementList *el) {
    start(QLatin1String("ClassElementList isStatic=%1")
          .arg(boolStr(el->isStatic)));
    return true;
}
void AstDumper::endVisit(AST::ClassElementList *) { stop("ClassElementList"); }

bool AstDumper::visit(AST::Program *) {
    start(QLatin1String("Program"));
    return true;
}
void AstDumper::endVisit(AST::Program *) { stop("Program"); }

bool AstDumper::visit(AST::NameSpaceImport *el) {
    start(QLatin1String("NameSpaceImport starToken=%1 importedBindingToken=%2 importedBinding=%3")
          .arg(loc(el->starToken), loc(el->importedBindingToken), qs(el->importedBinding)));
    return true;
}
void AstDumper::endVisit(AST::NameSpaceImport *) { stop("NameSpaceImport"); }

bool AstDumper::visit(AST::ImportSpecifier *el) {
    start(QLatin1String("ImportSpecifier identifierToken=%1 importedBindingToken=%2 identifier=%3 importedBinding=%4")
          .arg(loc(el->identifierToken), loc(el->importedBindingToken), qs(el->identifier), qs(el->importedBinding)));
    return true;
}
void AstDumper::endVisit(AST::ImportSpecifier *) { stop("ImportSpecifier"); }

bool AstDumper::visit(AST::ImportsList *el) {
    start(QLatin1String("ImportsList importSpecifierToken=%1")
          .arg(loc(el->importSpecifierToken)));
    return true;
}
void AstDumper::endVisit(AST::ImportsList *) { stop("ImportsList"); }

bool AstDumper::visit(AST::NamedImports *el) {
    start(QLatin1String("NamedImports leftBraceToken=%1 rightBraceToken=%2")
          .arg(loc(el->leftBraceToken), loc(el->rightBraceToken)));
    return true;
}
void AstDumper::endVisit(AST::NamedImports *) { stop("NamedImports"); }

bool AstDumper::visit(AST::FromClause *el) {
    start(QLatin1String("FromClause fromToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3")
          .arg(loc(el->fromToken), loc(el->moduleSpecifierToken), qs(el->moduleSpecifier)));
    return true;
}
void AstDumper::endVisit(AST::FromClause *) { stop("FromClause"); }

bool AstDumper::visit(AST::ImportClause *el) {
    start(QLatin1String("ImportClause importedDefaultBindingToken=%1 importedDefaultBinding=%2")
          .arg(loc(el->importedDefaultBindingToken), qs(el->importedDefaultBinding)));
    return true;
}
void AstDumper::endVisit(AST::ImportClause *) { stop("ImportClause"); }

bool AstDumper::visit(AST::ImportDeclaration *el) {
    start(QLatin1String("ImportDeclaration importToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3")
          .arg(loc(el->importToken), loc(el->moduleSpecifierToken), qs(el->moduleSpecifier)));
    return true;
}
void AstDumper::endVisit(AST::ImportDeclaration *) { stop("ImportDeclaration"); }

bool AstDumper::visit(AST::ExportSpecifier *el) {
    start(QLatin1String("ExportSpecifier identifierToken=%1 exportedIdentifierToken=%2 identifier=%3 exportedIdentifier=%4")
          .arg(loc(el->identifierToken), loc(el->exportedIdentifierToken), qs(el->identifier), qs(el->exportedIdentifier)));
    return true;
}
void AstDumper::endVisit(AST::ExportSpecifier *) { stop("ExportSpecifier"); }

bool AstDumper::visit(AST::ExportsList *) {
    start(QLatin1String("ExportsList"));
    return true;
}
void AstDumper::endVisit(AST::ExportsList *) { stop("ExportsList"); }

bool AstDumper::visit(AST::ExportClause *el) {
    start(QLatin1String("ExportClause leftBraceToken=%1 rightBraceToken=%2")
          .arg(loc(el->leftBraceToken), loc(el->rightBraceToken)));
    return true;
}
void AstDumper::endVisit(AST::ExportClause *) { stop("ExportClause"); }

bool AstDumper::visit(AST::ExportDeclaration *el) {
    start(QLatin1String("ExportDeclaration exportToken=%1 exportAll=%2 exportDefault=%3")
          .arg(loc(el->exportToken), boolStr(el->exportAll), boolStr(el->exportDefault)));
    return true;
}
void AstDumper::endVisit(AST::ExportDeclaration *) { stop("ExportDeclaration"); }

bool AstDumper::visit(AST::ESModule *) {
    start(QLatin1String("ESModule"));
    return true;
}
void AstDumper::endVisit(AST::ESModule *) { stop("ESModule"); }

bool AstDumper::visit(AST::DebuggerStatement *el) {
    start(QLatin1String("DebuggerStatement debuggerToken=%1 semicolonToken=%2")
          .arg(loc(el->debuggerToken), loc(el->semicolonToken)));
    return true;
}
void AstDumper::endVisit(AST::DebuggerStatement *) { stop("DebuggerStatement"); }

bool AstDumper::visit(AST::Type *) {
    start(QLatin1String("Type"));
    return true;
}
void AstDumper::endVisit(AST::Type *) { stop("Type"); }

bool AstDumper::visit(AST::TypeArgumentList *) {
    start(QLatin1String("TypeArgumentList"));
    return true;
}
void AstDumper::endVisit(AST::TypeArgumentList *) { stop("TypeArgumentList"); }

bool AstDumper::visit(AST::TypeAnnotation *el) {
    start(QLatin1String("TypeAnnotation colonToken=%1")
          .arg(loc(el->colonToken)));
    return true;
}
void AstDumper::endVisit(AST::TypeAnnotation *) { stop("TypeAnnotation"); }

void AstDumper::throwRecursionDepthError()
{
    qDebug() << "Maximum statement or expression depth exceeded in AstDumper";
}

bool AstDumper::dumpNode() {
    return options & DumperOptions::DumpNode;
}

bool AstDumper::noLocations() {
    return options & DumperOptions::NoLocations;
}

bool AstDumper::noAnnotations() {
    return options & DumperOptions::NoAnnotations;
}

} // end namespace QQmlJS

QT_END_NAMESPACE
