23#include <QRegularExpression>
26using namespace Qt::StringLiterals;
31const char *
QgsEsriArcadeBinaryOperatorExpression::BINARY_OPERATOR_TEXT[] = {
"+",
"-",
"*",
"/",
"%",
"==",
"!=",
"<",
">",
"<=",
">=",
"&&",
"||",
"&",
"|",
"^",
"<<",
">>",
">>>" };
47 qDeleteAll( mStatements );
52 mStatements.append( statement );
58 parts.reserve( mStatements.size() );
61 parts << statement->dump();
63 return parts.join(
'\n' );
68 QList<QgsEsriArcadeStatement *> clonedList;
69 clonedList.reserve( mStatements.size() );
79 visitor.
visit( *
this );
94 return u
"var %1 = %2;"_s.arg( mVariableName, mInitializer->dump() );
96 return u
"var %1;"_s.arg( mVariableName );
106 visitor.
visit( *
this );
121 return u
"%1 %2 %3;"_s.arg( mTarget ? mTarget->dump() : QString(),
ASSIGNMENT_OPERATOR_TEXT[
static_cast<int>( mAssignmentOperator )], mValue ? mValue->dump() : QString() );
133 visitor.
visit( *
this );
147 return u
"return %1;"_s.arg( mExpression->dump() );
159 visitor.
visit( *
this );
176 body = mBody->dump();
178 return u
"function %1(%2) {\n%3\n}"_s.arg( mFunctionName, mParameters.join(
", "_L1 ),
body );
188 visitor.
visit( *
this );
202 return u
"%1;"_s.arg( mExpression->dump() );
214 visitor.
visit( *
this );
230 switch ( mValue.userType() )
232 case QMetaType::Type::Int:
233 return QString::number( mValue.toInt() );
234 case QMetaType::Type::LongLong:
235 return QString::number( mValue.toLongLong() );
236 case QMetaType::Type::Double:
238 const double value = mValue.toDouble();
239 if ( std::isnan(
value ) )
241 if ( std::isinf(
value ) )
242 return value < 0 ? u
"-Infinity"_s : u
"Infinity"_s;
245 case QMetaType::Type::QString:
246 return mValue.toString();
247 case QMetaType::Type::Bool:
248 return mValue.toBool() ? u
"true"_s : u
"false"_s;
250 return mValue.toString();
259 if ( mValue.userType() == QMetaType::Type::QString )
272 visitor.
visit( *
this );
295 visitor.
visit( *
this );
318 visitor.
visit( *
this );
333 return u
"%1%2%3"_s.arg( mContainerExpression ? mContainerExpression->dump() : QString(), mIsSafeAccess ?
"?."_L1 :
"."_L1, mMemberName );
344 visitor.
visit( *
this );
364 switch ( mBinaryOperator )
409 const QString leftDump = leftBinary && ( leftBinary->
precedence() <
precedence() ) ? u
"(%1)"_s.arg( mLeftOperand->dump() ) : mLeftOperand->
dump();
411 const QString rightDump = rightBinary && ( rightBinary->
precedence() <=
precedence() ) ? u
"(%1)"_s.arg( mRightOperand->dump() ) : mRightOperand->
dump();
413 return u
"%1 %2 %3"_s.arg( leftDump,
text(), rightDump );
425 visitor.
visit( *
this );
445 return u
"%1%2"_s.arg( mOperand ? mOperand->dump() : QString(),
text() );
447 return u
"%1%2"_s.arg(
text(), mOperand ? mOperand->dump() : QString() );
457 visitor.
visit( *
this );
471 qDeleteAll( mArguments );
476 QStringList argumentStrings;
477 argumentStrings.reserve( mArguments.size() );
480 argumentStrings.append( argument->dump() );
482 return u
"%1(%2)"_s.arg( mFunctionName, argumentStrings.join(
", "_L1 ) );
487 QList<QgsEsriArcadeExpression *> clonedArguments;
488 clonedArguments.reserve( mArguments.size() );
498 visitor.
visit( *
this );
512 return u
"%1[%2]"_s.arg( mContainerExpression ? mContainerExpression->dump() : QString(), mIndexExpression ? mIndexExpression->dump() : QString() );
524 visitor.
visit( *
this );
537 qDeleteAll( mElements );
542 QStringList elementStrings;
543 elementStrings.reserve( mElements.size() );
546 elementStrings.append( element->dump() );
548 return u
"[%1]"_s.arg( elementStrings.join(
", "_L1 ) );
553 QList<QgsEsriArcadeExpression *> clonedElements;
554 clonedElements.reserve( mElements.size() );
564 visitor.
visit( *
this );
603 qDeleteAll( mElements );
608 if ( mElements.isEmpty() )
611 QStringList elementStrings;
612 elementStrings.reserve( mElements.size() );
615 elementStrings.append( element->dump() );
617 return u
"{ %1 }"_s.arg( elementStrings.join(
", "_L1 ) );
622 QList<QgsEsriArcadeDictionaryElement *> clonedElements;
623 clonedElements.reserve( mElements.size() );
626 clonedElements.append( element->clone() );
633 visitor.
visit( *
this );
649 QString result = u
"if (%1) {\n%2}"_s.arg( mCondition ? mCondition->dump() : QString(), mTrueBlock ? mTrueBlock->dump() : QString() );
653 result += u
" else %1"_s.arg( mFalseBlock->dump() );
655 result += u
" else {\n%1}"_s.arg( mFalseBlock->dump() );
670 visitor.
visit( *
this );
687 if ( mInitialization )
689 initDump = mInitialization->dump().trimmed();
690 if ( initDump.endsWith(
';'_L1 ) )
694 return u
"for (%1; %2; %3) {\n%4}"_s.arg( initDump, mCondition ? mCondition->dump() : QString(), mStep ? mStep->dump() : QString(), mBody ? mBody->dump() : QString() );
708 visitor.
visit( *
this );
728 return u
"for (%1%2 %3 %4) {\n%5}"_s.arg( mIsVariableDeclaration ?
"var "_L1 :
""_L1, mVariableName, keyword, mCollection ? mCollection->dump() : QString(), mBody ? mBody->dump() : QString() );
740 visitor.
visit( *
this );
754 return u
"while (%1) {\n%2}"_s.arg( mCondition ? mCondition->dump() : QString(), mBody ? mBody->dump() : QString() );
766 visitor.
visit( *
this );
785 visitor.
visit( *
this );
794 return u
"continue;"_s;
804 visitor.
visit( *
this );
814 const QList<QgsEsriArcadeStatement *> statements = node.
statements();
817 statement->accept( *
this );
884 const QList<QgsEsriArcadeExpression *> arguments = node.
arguments();
887 argument->accept( *
this );
901 const QList<QgsEsriArcadeExpression *> elements = node.
elements();
904 element->accept( *
this );
910 const QList<QgsEsriArcadeDictionaryElement *> elements = node.
elements();
913 if ( element->value() )
914 element->value()->accept( *
this );
969 if ( !scriptText.isEmpty() )
971 mRootBlock.reset( parseArcadeScript( scriptText, mParserErrorString ) );
981 mRootBlock.reset( dynamic_cast<QgsEsriArcadeBlockStatement *>( other.mRootBlock->clone() ) );
987 if (
this != &other )
1044 QString escaped = text;
1045 escaped.replace(
'\\'_L1,
"\\\\"_L1 );
1046 escaped.replace(
'\"'_L1,
"\\\""_L1 );
1047 escaped.replace(
'\n'_L1,
"\\n"_L1 );
1048 escaped.replace(
'\t'_L1,
"\\t"_L1 );
1049 escaped.replace(
'\r'_L1,
"\\r"_L1 );
1050 return u
"\"%1\""_s.arg( escaped );
Represents an array literal expression.
QgsEsriArcadeArrayLiteralExpression(const QList< QgsEsriArcadeExpression * > &elements)
Constructor for QgsEsriArcadeArrayLiteralExpression.
~QgsEsriArcadeArrayLiteralExpression() override
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QList< QgsEsriArcadeExpression * > elements() const
Returns the list of element expressions.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString dump() const override
Returns the formatted script representation of the node.
Represents a variable reassignment statement.
AssignmentOperator
Assignment operator types.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeExpression * value() const
Returns the assigned value expression.
static const char * ASSIGNMENT_OPERATOR_TEXT[]
QgsEsriArcadeAssignmentStatement(AssignmentOperator assignmentOperator, QgsEsriArcadeExpression *target, QgsEsriArcadeExpression *value)
Constructor for QgsEsriArcadeAssignmentStatement.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeExpression * target() const
Returns the target expression being assigned to.
AssignmentOperator assignmentOperator() const
Returns the assignment operator.
Represents a binary operation expression.
QgsEsriArcadeBinaryOperatorExpression(BinaryOperator binaryOperator, QgsEsriArcadeExpression *leftOperand, QgsEsriArcadeExpression *rightOperand)
Constructor for QgsEsriArcadeBinaryOperatorExpression.
BinaryOperator binaryOperator() const
Returns the binary operator.
static const char * BINARY_OPERATOR_TEXT[]
QString dump() const override
Returns the formatted script representation of the node.
BinaryOperator
Binary operator enumeration.
@ LogicalAnd
Logical and &&.
@ NotEqual
Not equal test !=.
@ GreaterThanOrEqual
Greater than or equal to >=.
@ LessThanOrEqual
Less than or equal to <=.
@ BitwiseAnd
Bitwise and, eg "7 & 14".
@ BitwiseSignedRightShift
Bitwise right shift, eg "7 >> 14".
@ LogicalOr
Logical or ||.
@ BitwiseOr
Bitwise or, eg "7 | 14".
@ BitwiseLeftShift
Bitwise left shift, eg "7 << 14".
@ BitwiseXor
Bitwise xor, eg "7 ^ 14".
@ Multiply
Multiplication *.
@ BitwiseUnsignedRightShift
Bitwise unsigned right shift, eg "7 >>> 14".
@ GreaterThan
Greater than >.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeExpression * rightOperand() const
Returns the right-hand operand expression.
bool leftAssociative() const
Returns true if operator is left-associative.
QgsEsriArcadeExpression * leftOperand() const
Returns the left-hand operand expression.
int precedence() const
Returns operator precedence value.
QString text() const
Returns text representation of the operator.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
Represents a sequence or block of statements enclosed in braces.
~QgsEsriArcadeBlockStatement() override
QgsEsriArcadeBlockStatement()=default
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QString dump() const override
Returns the formatted script representation of the node.
QList< QgsEsriArcadeStatement * > statements() const
Returns the list of statements contained within this block.
void appendStatement(QgsEsriArcadeStatement *statement)
Appends a statement to the block.
Represents a break loop control statement.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QgsEsriArcadeBreakStatement()=default
Constructor for QgsEsriArcadeBreakStatement.
QString dump() const override
Returns the formatted script representation of the node.
Represents a continue loop control statement.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeContinueStatement()=default
Constructor for QgsEsriArcadeContinueStatement.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
Represents a key-value entry in an Esri Arcade dictionary literal.
QgsEsriArcadeDictionaryElement * clone() const
Creates a deep copy of the element.
QString dump() const
Formats the element as string.
~QgsEsriArcadeDictionaryElement()
QString key() const
Returns the entry key.
bool isAutoKey() const
Returns true if the entry was defined using auto-keys.
QgsEsriArcadeExpression * value() const
Returns the entry value expression.
QgsEsriArcadeDictionaryElement(const QString &key, QgsEsriArcadeExpression *value, bool isAutoKey=false)
Constructor for QgsEsriArcadeDictionaryElement.
Represents a dictionary literal expression.
QgsEsriArcadeDictionaryLiteralExpression(const QList< QgsEsriArcadeDictionaryElement * > &elements)
Constructor for QgsEsriArcadeDictionaryLiteralExpression.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
~QgsEsriArcadeDictionaryLiteralExpression() override
QString dump() const override
Returns the formatted script representation of the node.
QList< QgsEsriArcadeDictionaryElement * > elements() const
Returns the dictionary elements.
Represents an expression evaluated as a statement.
QgsEsriArcadeExpressionStatement(QgsEsriArcadeExpression *expression)
Constructor for QgsEsriArcadeExpressionStatement.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeExpression * expression() const
Returns the underlying expression.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
Base class for expression nodes in an Esri Arcade script.
Represents a range based for...in or for...of iteration statement.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
IterationType iterationType() const
Returns the iteration type (for...in or for...of).
QgsEsriArcadeForInStatement(IterationType iterationType, const QString &variableName, bool isVariableDeclaration, QgsEsriArcadeExpression *collection, QgsEsriArcadeBlockStatement *body)
Constructor for QgsEsriArcadeForInStatement.
QgsEsriArcadeExpression * collection() const
Returns the collection expression being iterated over.
QgsEsriArcadeBlockStatement * body() const
Returns the loop body block statement.
QString dump() const override
Returns the formatted script representation of the node.
IterationType
Type of iteration loop.
QString variableName() const
Returns the loop variable identifier name.
bool isVariableDeclaration() const
Returns true if the loop variable was declared with the var keyword.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
Represents a standard counter-based for loop.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QgsEsriArcadeNode * step() const
Returns the step expression or statement, or nullptr if none.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeBlockStatement * body() const
Returns the loop body block statement.
QgsEsriArcadeStatement * initialization() const
Returns the initialization statement, or nullptr if none.
QgsEsriArcadeForStatement(QgsEsriArcadeStatement *initialization, QgsEsriArcadeExpression *condition, QgsEsriArcadeNode *step, QgsEsriArcadeBlockStatement *body)
Constructor for QgsEsriArcadeForStatement.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeExpression * condition() const
Returns the loop condition expression, or nullptr if none.
Represents a function call expression.
QList< QgsEsriArcadeExpression * > arguments() const
Returns the list of argument expressions.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString functionName() const
Returns the function name.
QString dump() const override
Returns the formatted script representation of the node.
~QgsEsriArcadeFunctionCallExpression() override
QgsEsriArcadeFunctionCallExpression(const QString &functionName, const QList< QgsEsriArcadeExpression * > &arguments)
Constructor for QgsEsriArcadeFunctionCallExpression.
Represents a function declaration statement.
QString functionName() const
Returns the function name.
QStringList parameters() const
Returns the parameter names.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeBlockStatement * body() const
Returns the function body block.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QgsEsriArcadeFunctionDeclarationStatement(const QString &functionName, const QStringList ¶meters, QgsEsriArcadeBlockStatement *body)
Constructor for QgsEsriArcadeFunctionDeclarationStatement.
Represents an if statement.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeExpression * condition() const
Returns the condition expression.
QgsEsriArcadeIfStatement(QgsEsriArcadeExpression *condition, QgsEsriArcadeBlockStatement *trueBlock, QgsEsriArcadeBlockStatement *falseBlock=nullptr)
Constructor for QgsEsriArcadeIfStatement.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeBlockStatement * falseBlock() const
Returns the statement block executed when the condition evaluates to false, or nullptr if none.
QgsEsriArcadeBlockStatement * trueBlock() const
Returns the statement block executed when the condition evaluates to true.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
Represents an array or dictionary index subscript expression.
QgsEsriArcadeExpression * indexExpression() const
Returns the index expression.
QgsEsriArcadeIndexExpression(QgsEsriArcadeExpression *containerExpression, QgsEsriArcadeExpression *indexExpression)
Constructor for QgsEsriArcadeIndexExpression.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeExpression * containerExpression() const
Returns the container expression being indexed.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
Represents a literal value (number, text string, boolean, or null).
QString dump() const override
Returns the formatted script representation of the node.
QVariant value() const
Returns the literal value.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString valueAsString() const
Returns a string representation of the literal value.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeLiteralExpression(const QVariant &value)
Constructor for QgsEsriArcadeLiteralExpression.
Represents a member access expression using dot or bracket notation.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
bool isSafeAccess() const
Returns true if member access uses the safe access operator "?.".
QgsEsriArcadeExpression * containerExpression() const
Returns the container expression being accessed.
QString memberName() const
Returns the member attribute name.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeMemberAccessExpression(QgsEsriArcadeExpression *containerExpression, const QString &memberName, bool isSafeAccess=false)
Constructor for QgsEsriArcadeMemberAccessExpression.
Represents an abstract node for an Esri Arcade script.
virtual void accept(QgsEsriArcadeVisitor &visitor) const =0
Accepts a visitor to traverse the node.
virtual QgsEsriArcadeNode * clone() const =0
Creates a copy of the node.
@ IfStatement
If statement.
Represents a reference to a profile variable.
QgsEsriArcadeProfileVariableReferenceExpression(const QString &name)
Constructor for QgsEsriArcadeProfileVariableReferenceExpression.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString dump() const override
Returns the formatted script representation of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QString name() const
Returns the profile variable name, including the leading $ symbol.
void visit(const QgsEsriArcadeBlockStatement &node) override
Visit a QgsEsriArcadeBlockStatement node.
Represents a return statement.
QgsEsriArcadeReturnStatement(QgsEsriArcadeExpression *expression=nullptr)
Constructor for QgsEsriArcadeReturnStatement.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeExpression * expression() const
Returns the expression returned by the statement, or nullptr if void return.
QString dump() const
Dumps the abstract syntax tree back into an Arcade script string.
std::unique_ptr< QgsEsriArcadeBlockStatement > mRootBlock
void acceptVisitor(QgsEsriArcadeVisitor &visitor) const
Accepts a visitor to traverse the script abstract syntax tree.
const QgsEsriArcadeBlockStatement * rootBlock() const
Returns the root block statement node, or nullptr if parsing failed.
QgsEsriArcadeScript & operator=(const QgsEsriArcadeScript &other)
bool hasParserError() const
Returns true if a parsing error occurred.
static QString quotedString(const QString &text)
Returns a properly quoted string literal (enclosed in double quotes with escape sequences).
QgsEsriArcadeScript(const QString &scriptText=QString())
Creates a new script from scriptText.
QString script() const
Returns the original script text.
QString parserErrorString() const
Returns parser error description.
QString mParserErrorString
Base class for statement nodes in an Esri Arcade script.
Represents a unary operation expression.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeUnaryOperatorExpression(UnaryOperator unaryOperator, QgsEsriArcadeExpression *operand)
Constructor for QgsEsriArcadeUnaryOperatorExpression.
QgsEsriArcadeExpression * operand() const
Returns the operand expression.
QString text() const
Returns text representation of the operator.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
UnaryOperator unaryOperator() const
Returns the unary operator.
static const char * UNARY_OPERATOR_TEXT[]
QString dump() const override
Returns the formatted script representation of the node.
UnaryOperator
Unary operator enumeration.
@ PostIncrement
Postincrement operator, eg "x++".
@ PostDecrement
Postdecrement operator, eg "x--".
Represents a variable declaration statement.
QString dump() const override
Returns the formatted script representation of the node.
QgsEsriArcadeVariableDeclarationStatement(const QString &variableName, QgsEsriArcadeExpression *initializer=nullptr)
Constructor for QgsEsriArcadeVariableDeclarationStatement.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeExpression * initializer() const
Returns the optional initializer expression, or nullptr if uninitialized.
QString variableName() const
Returns the declared variable name.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
Represents a reference to a variable identifier.
QString dump() const override
Returns the formatted script representation of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QString name() const
Returns the variable identifier name.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
QgsEsriArcadeVariableReferenceExpression(const QString &name)
Constructor for QgsEsriArcadeVariableReferenceExpression.
Abstract visitor interface for traversing Esri Arcade abstract syntax trees.
virtual void visit(const QgsEsriArcadeBlockStatement &node)=0
Visit a QgsEsriArcadeBlockStatement node.
Represents a while loop statement.
QgsEsriArcadeBlockStatement * body() const
Returns the loop body block statement.
QgsEsriArcadeWhileStatement(QgsEsriArcadeExpression *condition, QgsEsriArcadeBlockStatement *body)
Constructor for QgsEsriArcadeWhileStatement.
QgsEsriArcadeExpression * condition() const
Returns the loop condition expression.
QString dump() const override
Returns the formatted script representation of the node.
void accept(QgsEsriArcadeVisitor &visitor) const override
Accepts a visitor to traverse the node.
QgsEsriArcadeNode * clone() const override
Creates a copy of the node.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QgsEsriArcadeBlockStatement * parseArcadeScript(const QString &scriptText, QString &parserErrorMessage)