23using namespace Qt::StringLiterals;
31 static const QString NEXT_BLOCK_MARKER;
34 bool mCurrentPartIsLiteral =
false;
35 bool mCurrentPartIsSimple =
false;
37 void visit(
const QgsEsriArcadeNode &node )
39 if (
auto block =
dynamic_cast< const QgsEsriArcadeBlockStatement *
>( &node ) )
43 else if (
auto varDeclare =
dynamic_cast< const QgsEsriArcadeVariableDeclarationStatement *
>( &node ) )
47 else if (
auto varAssign =
dynamic_cast< const QgsEsriArcadeAssignmentStatement *
>( &node ) )
51 else if (
auto returnStatement =
dynamic_cast< const QgsEsriArcadeReturnStatement *
>( &node ) )
53 visit( *returnStatement );
55 else if (
auto functionDef =
dynamic_cast< const QgsEsriArcadeFunctionDeclarationStatement *
>( &node ) )
57 visit( *functionDef );
59 else if (
auto expression =
dynamic_cast< const QgsEsriArcadeExpressionStatement *
>( &node ) )
63 else if (
auto literal =
dynamic_cast< const QgsEsriArcadeLiteralExpression *
>( &node ) )
67 else if (
auto variableReference =
dynamic_cast< const QgsEsriArcadeVariableReferenceExpression *
>( &node ) )
69 visit( *variableReference );
71 else if (
auto profileVariableReference =
dynamic_cast< const QgsEsriArcadeProfileVariableReferenceExpression *
>( &node ) )
73 visit( *profileVariableReference );
75 else if (
auto memberAccess =
dynamic_cast< const QgsEsriArcadeMemberAccessExpression *
>( &node ) )
77 visit( *memberAccess );
79 else if (
auto binaryOperator =
dynamic_cast< const QgsEsriArcadeBinaryOperatorExpression *
>( &node ) )
81 visit( *binaryOperator );
83 else if (
auto unaryOperator =
dynamic_cast< const QgsEsriArcadeUnaryOperatorExpression *
>( &node ) )
85 visit( *unaryOperator );
87 else if (
auto functionCall =
dynamic_cast< const QgsEsriArcadeFunctionCallExpression *
>( &node ) )
89 visit( *functionCall );
91 else if (
auto index =
dynamic_cast< const QgsEsriArcadeIndexExpression *
>( &node ) )
95 else if (
auto arrayLiteral =
dynamic_cast< const QgsEsriArcadeArrayLiteralExpression *
>( &node ) )
97 visit( *arrayLiteral );
99 else if (
auto dictionaryLiteral =
dynamic_cast< const QgsEsriArcadeDictionaryLiteralExpression *
>( &node ) )
101 visit( *dictionaryLiteral );
103 else if (
auto ifStatement =
dynamic_cast< const QgsEsriArcadeIfStatement *
>( &node ) )
105 visit( *ifStatement );
107 else if (
auto forStatement =
dynamic_cast< const QgsEsriArcadeForStatement *
>( &node ) )
109 visit( *forStatement );
111 else if (
auto forInStatement =
dynamic_cast< const QgsEsriArcadeForInStatement *
>( &node ) )
113 visit( *forInStatement );
115 else if (
auto whileStatement =
dynamic_cast< const QgsEsriArcadeWhileStatement *
>( &node ) )
117 visit( *whileStatement );
119 else if (
auto breakStatement =
dynamic_cast< const QgsEsriArcadeBreakStatement *
>( &node ) )
121 visit( *breakStatement );
123 else if (
auto continueStatement =
dynamic_cast< const QgsEsriArcadeContinueStatement *
>( &node ) )
125 visit( *continueStatement );
129 throw QgsNotSupportedException( QObject::tr(
"An unexpected node was encountered while converting to a QGIS expression." ) );
133 void visit(
const QgsEsriArcadeBlockStatement &node )
override
138 throw QgsNotSupportedException( QObject::tr(
"An empty script block was encountered while converting to a QGIS expression." ) );
143 throw QgsNotSupportedException( QObject::tr(
"Scripts containing multiple statements cannot be converted to QGIS expressions." ) );
146 QgsEsriArcadeStatement *firstStatement = node.
statements().at( 0 );
147 if ( !firstStatement )
150 throw QgsNotSupportedException( QObject::tr(
"An empty script block was encountered while converting to a QGIS expression." ) );
152 visit( *firstStatement );
155 void visit(
const QgsEsriArcadeVariableDeclarationStatement & )
override
158 throw QgsNotSupportedException( QObject::tr(
"Scripts containing variable declarations cannot be converted to QGIS expressions." ) );
161 void visit(
const QgsEsriArcadeAssignmentStatement & )
override
164 throw QgsNotSupportedException( QObject::tr(
"Scripts containing variable assignments cannot be converted to QGIS expressions." ) );
167 void visit(
const QgsEsriArcadeFunctionDeclarationStatement & )
override
170 throw QgsNotSupportedException( QObject::tr(
"Scripts containing function definitions cannot be converted to QGIS expressions." ) );
173 void visit(
const QgsEsriArcadeReturnStatement &node )
override
175 if ( QgsEsriArcadeExpression *expression = node.
expression() )
177 visit( *expression );
181 mCurrentPart = u
"NULL"_s;
182 mCurrentPartIsLiteral =
false;
183 mCurrentPartIsSimple =
true;
187 void visit(
const QgsEsriArcadeExpressionStatement &node )
override
189 if ( QgsEsriArcadeExpression *expression = node.
expression() )
191 visit( *expression );
195 mCurrentPart = u
"NULL"_s;
196 mCurrentPartIsLiteral =
false;
197 mCurrentPartIsSimple =
true;
201 void visit(
const QgsEsriArcadeLiteralExpression &node )
override
203 const QVariant value = node.
value();
204 if ( value.toString() ==
"\n" )
206 mCurrentPart = u
"'\\n'"_s;
207 mCurrentPartIsLiteral =
false;
209 else if ( value.type() == QVariant::String )
212 mCurrentPartIsLiteral =
true;
216 mCurrentPart = u
"NULL"_s;
217 mCurrentPartIsLiteral =
false;
221 mCurrentPart = value.toString();
222 mCurrentPartIsLiteral =
true;
224 mCurrentPartIsSimple =
true;
227 void visit(
const QgsEsriArcadeVariableReferenceExpression &node )
override
229 const QString variableName = node.
name();
230 if ( variableName.isEmpty() )
233 throw QgsNotSupportedException( QObject::tr(
"An empty variable reference was encountered while converting to a QGIS expression." ) );
235 mCurrentPart = u
"@%1"_s.arg( variableName );
236 mCurrentPartIsLiteral =
false;
237 mCurrentPartIsSimple =
true;
240 void visit(
const QgsEsriArcadeProfileVariableReferenceExpression &node )
override
242 const QString variableName = node.
name();
243 if ( variableName.compare(
"$feature"_L1, Qt::CaseInsensitive ) == 0 )
245 mCurrentPart = u
"@feature"_s;
246 mCurrentPartIsLiteral =
false;
247 mCurrentPartIsSimple =
true;
249 else if ( variableName.compare(
"$view"_L1, Qt::CaseInsensitive ) == 0 )
252 mCurrentPart = variableName;
253 mCurrentPartIsLiteral =
false;
254 mCurrentPartIsSimple =
true;
259 throw QgsNotSupportedException( QObject::tr(
"Converting the %1 profile variable is not supported" ).arg( variableName ) );
263 void visit(
const QgsEsriArcadeMemberAccessExpression &node )
override
272 throw QgsNotSupportedException( QObject::tr(
"Member access to an empty container was encountered while converting to a QGIS expression." ) );
275 if ( mCurrentPart.compare(
"@feature"_L1, Qt::CaseInsensitive ) == 0 )
278 mCurrentPartIsLiteral =
false;
279 mCurrentPartIsSimple =
true;
281 else if ( mCurrentPart.compare(
"$view"_L1, Qt::CaseInsensitive ) == 0 )
283 static const QMap< QString, QString > sVIEW_VARS {
284 { u
"scale"_s, u
"@map_scale"_s },
286 auto it = sVIEW_VARS.constFind( node.
memberName().toLower() );
287 if ( it != sVIEW_VARS.constEnd() )
289 mCurrentPart = it.value();
290 mCurrentPartIsLiteral =
false;
291 mCurrentPartIsSimple =
true;
296 throw QgsNotSupportedException( QObject::tr(
"Converting the %1 view member is not supported" ).arg( node.
memberName() ) );
302 throw QgsNotSupportedException( QObject::tr(
"Converting the member access operator in this expression is not supported" ) );
306 void visit(
const QgsEsriArcadeBinaryOperatorExpression &node )
override
311 throw QgsNotSupportedException( QObject::tr(
"A binary operator with missing operands was encountered while converting to a QGIS expression." ) );
314 bool isChainableOp =
false;
320 isChainableOp =
true;
342 bool leftChainable =
false;
343 bool rightChainable =
false;
346 if (
dynamic_cast< QgsEsriArcadeBinaryOperatorExpression *
>( node.
leftOperand() )
347 &&
dynamic_cast< QgsEsriArcadeBinaryOperatorExpression *
>( node.
leftOperand() )->binaryOperator() == node.
binaryOperator() )
349 leftChainable =
true;
351 if (
dynamic_cast< QgsEsriArcadeBinaryOperatorExpression *
>( node.
rightOperand() )
352 &&
dynamic_cast< QgsEsriArcadeBinaryOperatorExpression *
>( node.
rightOperand() )->binaryOperator() == node.
binaryOperator() )
354 rightChainable =
true;
359 QString newOpLeftPart = mCurrentPart;
360 const bool leftIsSimple = mCurrentPartIsSimple;
363 QString newOpRightChunk = mCurrentPart;
364 const bool rightIsSimple = mCurrentPartIsSimple;
367 if ( newOpRightChunk.compare(
"NULL"_L1, Qt::CaseInsensitive ) == 0 )
373 newOpLeftPart = u
"(%1)"_s.arg( newOpLeftPart );
376 mCurrentPartIsLiteral =
false;
377 mCurrentPartIsSimple =
false;
382 static const QMap< QgsEsriArcadeBinaryOperatorExpression::BinaryOperator, QString > sBINARY_OPERATOR_MAP {
398 mCurrentPartIsSimple =
false;
399 auto it = sBINARY_OPERATOR_MAP.constFind( node.
binaryOperator() );
400 if ( it != sBINARY_OPERATOR_MAP.constEnd() )
402 if ( !leftIsSimple && !leftChainable )
404 newOpLeftPart = u
"(%1)"_s.arg( newOpLeftPart );
406 if ( !rightIsSimple && !rightChainable )
408 newOpRightChunk = u
"(%1)"_s.arg( newOpRightChunk );
410 mCurrentPart = u
"%1 %2 %3"_s.arg( newOpLeftPart, it.value(), newOpRightChunk );
411 mCurrentPartIsLiteral =
false;
412 mCurrentPartIsSimple =
false;
417 throw QgsNotSupportedException( QObject::tr(
"Converting the %1 operator is not supported" ).arg( node.
text() ) );
421 void visit(
const QgsEsriArcadeUnaryOperatorExpression &node )
override
426 throw QgsNotSupportedException( QObject::tr(
"A unary operator with missing operand was encountered while converting to a QGIS expression." ) );
431 static const QMap< QgsEsriArcadeUnaryOperatorExpression::UnaryOperator, QString > sUNARY_OPERATOR_MAP {
437 auto it = sUNARY_OPERATOR_MAP.constFind( node.
unaryOperator() );
438 if ( it != sUNARY_OPERATOR_MAP.constEnd() )
440 QString part = mCurrentPart;
441 if ( !mCurrentPartIsSimple )
443 part = u
"(%1)"_s.arg( part );
445 mCurrentPart = u
"%1%2"_s.arg( it.value(), part );
446 mCurrentPartIsLiteral =
false;
447 mCurrentPartIsSimple =
false;
452 throw QgsNotSupportedException( QObject::tr(
"Converting the %1 operator is not supported" ).arg( node.
text() ) );
456 void visit(
const QgsEsriArcadeFunctionCallExpression &node )
override
458 const QString functionName = node.
functionName().toLower();
459 if ( functionName.isEmpty() )
462 throw QgsNotSupportedException( QObject::tr(
"An empty function name was encountered while converting to a QGIS expression." ) );
464 QStringList convertedArguments;
465 QList< bool > convertedArgumentsAreSimple;
466 QList< bool > convertedArgumentsAreLiteral;
468 for ( QgsEsriArcadeExpression *arg : node.
arguments() )
473 throw QgsNotSupportedException( QObject::tr(
"An empty function argument encountered while converting to a QGIS expression." ) );
476 convertedArguments.append( mCurrentPart );
477 convertedArgumentsAreSimple.append( mCurrentPartIsSimple );
478 convertedArgumentsAreLiteral.append( mCurrentPartIsLiteral );
481 if ( functionName.compare(
"date"_L1, Qt::CaseInsensitive ) == 0 )
484 throw QgsNotSupportedException( QObject::tr(
"The Arcade Date function cannot be automatically converted to a QGIS expression." ) );
486 else if ( functionName.compare(
"length"_L1, Qt::CaseInsensitive ) == 0 )
489 throw QgsNotSupportedException( QObject::tr(
"The Arcade Length function cannot be automatically converted to a QGIS expression." ) );
491 else if ( functionName.compare(
"domainname"_L1, Qt::CaseInsensitive ) == 0 )
493 if ( convertedArguments.size() == 2 && convertedArguments.at( 0 ) ==
"@feature"_L1 )
495 if ( convertedArgumentsAreLiteral[1] )
497 if ( convertedArguments[1].size() < 2 )
500 throw QgsNotSupportedException( QObject::tr(
"A domainname function with invalid name argument was encountered while converting to a QGIS expression." ) );
502 mCurrentPart = u
"represent_value(%1)"_s.arg(
QgsExpression::quotedColumnRef( convertedArguments[1].mid( 1, convertedArguments[1].length() - 2 ) ) );
503 mCurrentPartIsLiteral =
false;
504 mCurrentPartIsSimple =
true;
510 throw QgsNotSupportedException( QObject::tr(
"Complex forms of the Arcade DomainName function cannot be automatically converted to QGIS expressions." ) );
512 else if ( functionName.compare(
"when"_L1, Qt::CaseInsensitive ) == 0 )
514 if ( convertedArguments.size() < 2 )
517 throw QgsNotSupportedException( QObject::tr(
"When function with < 2 arguments encountered while converting to a QGIS expression." ) );
519 QString res = u
"CASE WHEN %1 THEN %2"_s.arg( convertedArguments.value( 0 ), convertedArguments.value( 1 ) );
520 if ( convertedArguments.size() > 2 )
525 if ( i >= convertedArguments.size() - 1 )
527 res += u
" WHEN %1 THEN %2"_s.arg( convertedArguments.value( i ), convertedArguments.value( i + 1 ) );
532 if ( convertedArguments.size() % 2 == 1 )
534 res += u
" ELSE %1"_s.arg( convertedArguments.value( convertedArguments.size() - 1 ) );
540 mCurrentPartIsLiteral =
false;
541 mCurrentPartIsSimple =
false;
544 else if ( functionName.compare(
"decode"_L1, Qt::CaseInsensitive ) == 0 )
546 if ( convertedArguments.size() < 2 )
549 throw QgsNotSupportedException( QObject::tr(
"A decode function with < 2 arguments was encountered while converting to a QGIS expression." ) );
551 QStringList mapGetParts;
555 if ( i >= convertedArguments.size() - 1 )
559 mapGetParts.append( convertedArguments.at( i ) );
561 mapGetParts.append( convertedArguments.at( i + 1 ) );
565 const QString mapGetPart = u
"map_get(map(%1), %2)"_s.arg( mapGetParts.join(
", " ), convertedArguments.at( 0 ) );
567 QString res = mapGetPart;
568 if ( convertedArguments.size() % 2 == 0 )
570 res = u
"coalesce(%1, %2)"_s.arg( mapGetPart, convertedArguments.at( convertedArguments.size() - 1 ) );
573 mCurrentPartIsLiteral =
false;
574 mCurrentPartIsSimple =
false;
577 else if ( functionName.compare(
"isempty"_L1, Qt::CaseInsensitive ) == 0 )
579 if ( convertedArguments.size() == 1 )
581 if ( convertedArgumentsAreSimple[0] )
583 mCurrentPart = u
"%1 IS NULL"_s.arg( convertedArguments[0] );
587 mCurrentPart = u
"(%1) IS NULL"_s.arg( convertedArguments[0] );
589 mCurrentPartIsLiteral =
false;
590 mCurrentPartIsSimple =
false;
595 throw QgsNotSupportedException( QObject::tr(
"This form of the Arcade IsEmpty function cannot be automatically converted to QGIS expressions." ) );
597 else if ( functionName.compare(
"mid"_L1, Qt::CaseInsensitive ) == 0 )
599 if ( convertedArguments.empty() )
602 throw QgsNotSupportedException( QObject::tr(
"A mid function with no arguments was encountered while converting to a QGIS expression." ) );
604 QString start = convertedArguments.value( 1 );
606 int intStart = start.toInt( &intOk );
607 if ( convertedArgumentsAreLiteral.value( 1 ) && intOk )
609 start = QString::number( intStart + 1 );
613 start = u
"%1 + 1"_s.arg( start );
616 if ( convertedArguments.size() == 2 )
618 mCurrentPart = u
"substr(%1, %2)"_s.arg( convertedArguments.value( 0 ), start );
620 else if ( convertedArguments.size() == 3 )
622 mCurrentPart = u
"substr(%1, %2, %3)"_s.arg( convertedArguments.value( 0 ), start, convertedArguments.value( 2 ) );
627 throw QgsNotSupportedException( QObject::tr(
"This form of the 'mid' function cannot be converted to a QGIS expression." ) );
629 mCurrentPartIsLiteral =
false;
630 mCurrentPartIsSimple =
true;
633 else if ( functionName.compare(
"number"_L1, Qt::CaseInsensitive ) == 0 )
635 if ( convertedArguments.size() == 1 )
637 mCurrentPart = u
"to_real(%1)"_s.arg( convertedArguments.at( 0 ) );
638 mCurrentPartIsLiteral =
false;
639 mCurrentPartIsSimple =
true;
645 throw QgsNotSupportedException( QObject::tr(
"This form of the 'number' function cannot be converted to a QGIS expression." ) );
648 else if ( functionName.compare(
"text"_L1, Qt::CaseInsensitive ) == 0 )
650 if ( convertedArguments.size() == 1 )
652 mCurrentPart = u
"to_string(%1)"_s.arg( convertedArguments.at( 0 ) );
653 mCurrentPartIsLiteral =
false;
654 mCurrentPartIsSimple =
true;
660 throw QgsNotSupportedException( QObject::tr(
"This form of the 'text' function cannot be converted to a QGIS expression." ) );
663 else if ( functionName.compare(
"split"_L1, Qt::CaseInsensitive ) == 0 )
665 if ( convertedArguments.size() == 2 )
667 mCurrentPart = u
"string_to_array(%1, %2)"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 1 ) );
668 mCurrentPartIsLiteral =
false;
669 mCurrentPartIsSimple =
true;
675 throw QgsNotSupportedException( QObject::tr(
"This form of the 'split' function cannot be converted to a QGIS expression." ) );
678 else if ( functionName.compare(
"concatenate"_L1, Qt::CaseInsensitive ) == 0 )
680 if ( convertedArguments.size() == 1 )
682 mCurrentPart = u
"array_to_string(%1)"_s.arg( convertedArguments.at( 0 ) );
683 mCurrentPartIsLiteral =
false;
684 mCurrentPartIsSimple =
true;
687 else if ( convertedArguments.size() == 2 )
689 mCurrentPart = u
"array_to_string(%1, %2)"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 1 ) );
690 mCurrentPartIsLiteral =
false;
691 mCurrentPartIsSimple =
true;
697 throw QgsNotSupportedException( QObject::tr(
"This form of the 'concatenate' function cannot be converted to a QGIS expression." ) );
700 else if ( functionName.compare(
"find"_L1, Qt::CaseInsensitive ) == 0 )
702 if ( convertedArguments.size() == 2 )
704 mCurrentPart = u
"strpos(%1, %2)"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 1 ) );
705 mCurrentPartIsLiteral =
false;
706 mCurrentPartIsSimple =
true;
709 else if ( convertedArguments.size() == 3 )
711 bool arg2IsInt =
false;
712 const int arg2Int = convertedArguments.at( 2 ).toInt( &arg2IsInt );
713 if ( arg2IsInt && arg2Int == 0 )
715 mCurrentPart = u
"strpos(%1, %2)"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 1 ) );
716 mCurrentPartIsSimple =
true;
720 mCurrentPart = u
"strpos(substr(%1, %2), %3) + %4"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 2 ), convertedArguments.at( 1 ), convertedArguments.at( 2 ) );
721 mCurrentPartIsSimple =
false;
724 mCurrentPartIsLiteral =
false;
730 throw QgsNotSupportedException( QObject::tr(
"This form of the 'find' function cannot be converted to a QGIS expression." ) );
733 else if ( functionName.compare(
"replace"_L1, Qt::CaseInsensitive ) == 0 )
735 if ( convertedArguments.size() == 3 )
737 mCurrentPart = u
"replace(%1, %2, %3)"_s.arg( convertedArguments.at( 0 ), convertedArguments.at( 1 ), convertedArguments.at( 2 ) );
738 mCurrentPartIsLiteral =
false;
739 mCurrentPartIsSimple =
true;
745 throw QgsNotSupportedException( QObject::tr(
"This form of the 'replace' function cannot be converted to a QGIS expression." ) );
750 static const QMap< QString, QString > sARCADE_FUNCTIONS {
751 {
"upper",
"upper" },
752 {
"lower",
"lower" },
753 {
"proper",
"title" },
754 {
"defaultvalue",
"coalesce" },
755 {
"round",
"round" },
757 {
"count",
"length" },
758 {
"right",
"right" },
761 {
"includes",
"array_contains" },
768 {
"atan2",
"atan2" },
771 {
"random",
"randf" },
777 auto it = sARCADE_FUNCTIONS.constFind( functionName );
778 if ( it != sARCADE_FUNCTIONS.constEnd() )
780 if ( !convertedArguments.empty() )
782 mCurrentPart = u
"%1(%2)"_s.arg( it.value(), convertedArguments.join(
", " ) );
783 mCurrentPartIsLiteral =
false;
787 mCurrentPart = u
"%1()"_s.arg( it.value() );
788 mCurrentPartIsLiteral =
false;
790 mCurrentPartIsSimple =
true;
795 throw QgsNotSupportedException( QObject::tr(
"The %1 function cannot be converted to a QGIS expression." ).arg( functionName ) );
799 void visit(
const QgsEsriArcadeIndexExpression &node )
override
805 throw QgsNotSupportedException( QObject::tr(
"An index operator with no container was encountered while converting to a QGIS expression." ) );
808 const QString containerPart = mCurrentPart;
814 throw QgsNotSupportedException( QObject::tr(
"An index operator with no index was encountered while converting to a QGIS expression." ) );
818 if ( containerPart.compare(
"@feature"_L1, Qt::CaseInsensitive ) == 0 )
820 if ( mCurrentPartIsLiteral )
822 static const QMap< QString, QString > sSPECIAL_FEATURE_ATTRIBUTES {
823 {
"shape.stlength()",
"length(@geometry)" },
824 {
"shape.starea()",
"area(@geometry)" },
827 const QString candidate = mCurrentPart.size() > 2 ? mCurrentPart.toLower().mid( 1, mCurrentPart.length() - 2 ) : QString();
828 auto it = sSPECIAL_FEATURE_ATTRIBUTES.constFind( candidate );
829 if ( it != sSPECIAL_FEATURE_ATTRIBUTES.constEnd() )
831 mCurrentPart = it.value();
835 if ( mCurrentPart.startsWith(
"'" ) && mCurrentPart.size() > 2 )
837 mCurrentPart = mCurrentPart.mid( 1, mCurrentPart.length() - 2 );
842 mCurrentPartIsLiteral =
false;
843 mCurrentPartIsSimple =
true;
849 throw QgsNotSupportedException( QObject::tr(
"This script cannot be converted to a QGIS expression." ) );
852 else if ( mCurrentPartIsLiteral )
855 const int intValue = mCurrentPart.toInt( &ok );
858 mCurrentPart = u
"%1[%2]"_s.arg( mCurrentPart ).arg( intValue );
859 mCurrentPartIsLiteral =
false;
860 mCurrentPartIsSimple =
true;
866 throw QgsNotSupportedException( QObject::tr(
"This script cannot be converted to a QGIS expression." ) );
872 throw QgsNotSupportedException( QObject::tr(
"This script cannot be converted to a QGIS expression." ) );
876 void visit(
const QgsEsriArcadeArrayLiteralExpression &node )
override
879 for ( QgsEsriArcadeExpression *element : node.
elements() )
884 throw QgsNotSupportedException( QObject::tr(
"An array literal with a missing element was encountered while converting to a QGIS expression." ) );
887 parts.append( mCurrentPart );
890 mCurrentPart = u
"array(%1)"_s.arg( parts.join(
", "_L1 ) );
891 mCurrentPartIsLiteral =
false;
892 mCurrentPartIsSimple =
true;
895 void visit(
const QgsEsriArcadeDictionaryLiteralExpression & )
override
898 throw QgsNotSupportedException( QObject::tr(
"Converting dictionary literals is supported" ) );
901 void visit(
const QgsEsriArcadeIfStatement &node )
override
903 QgsEsriArcadeExpression *conditionExpression = node.
condition();
904 if ( !conditionExpression )
907 throw QgsNotSupportedException( QObject::tr(
"An if statement with no condition was encountered while converting to a QGIS expression." ) );
909 visit( *conditionExpression );
910 const QString condition = mCurrentPart;
912 QgsEsriArcadeBlockStatement *trueBlock = node.
trueBlock();
916 throw QgsNotSupportedException( QObject::tr(
"An if statement with no true block was encountered while converting to a QGIS expression." ) );
919 const QString whenTrue = mCurrentPart;
922 if ( QgsEsriArcadeBlockStatement *falseBlock = node.
falseBlock() )
924 visit( *falseBlock );
925 whenFalse = mCurrentPart;
926 mCurrentPart = u
"if(%1, %2, %3)"_s.arg( condition, whenTrue, whenFalse );
930 mCurrentPart = u
"if(%1, %2, %3)"_s.arg( condition, whenTrue, NEXT_BLOCK_MARKER );
933 mCurrentPartIsLiteral =
false;
934 mCurrentPartIsSimple =
true;
937 void visit(
const QgsEsriArcadeForStatement & )
override
940 throw QgsNotSupportedException( QObject::tr(
"Scripts containing for loops cannot be converted to QGIS expressions." ) );
943 void visit(
const QgsEsriArcadeForInStatement & )
override
946 throw QgsNotSupportedException( QObject::tr(
"Scripts containing for loops cannot be converted to QGIS expressions." ) );
949 void visit(
const QgsEsriArcadeWhileStatement & )
override
952 throw QgsNotSupportedException( QObject::tr(
"Scripts containing while loops cannot be converted to QGIS expressions." ) );
955 void visit(
const QgsEsriArcadeBreakStatement & )
override
958 throw QgsNotSupportedException( QObject::tr(
"Scripts containing break statements cannot be converted to QGIS expressions." ) );
961 void visit(
const QgsEsriArcadeContinueStatement & )
override
964 throw QgsNotSupportedException( QObject::tr(
"Scripts containing continue statements cannot be converted to QGIS expressions." ) );
968const QString ArcadeToExpressionVisitor::NEXT_BLOCK_MARKER = u
"!!!!!NEXT_BLOCK!!!!!"_s;
983 mErrorString = QObject::tr(
"The Arcade script could not be parsed." );
988 ArcadeToExpressionVisitor visitor;
992 mExpression = visitor.mCurrentPart;
996 mErrorString = ex.
what();
997 mResult = visitor.mResult;
QList< QgsEsriArcadeExpression * > elements() const
Returns the list of element expressions.
BinaryOperator binaryOperator() const
Returns the binary operator.
@ 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 >.
QgsEsriArcadeExpression * rightOperand() const
Returns the right-hand operand expression.
QgsEsriArcadeExpression * leftOperand() const
Returns the left-hand operand expression.
QString text() const
Returns text representation of the operator.
QList< QgsEsriArcadeStatement * > statements() const
Returns the list of statements contained within this block.
Result
Results of converting the Arcade script.
@ InvalidScriptMultiStatements
Scripts containing multiple statements cannot be converted.
@ Success
Script was successfully converted.
@ ParserError
Script could not be parsed.
@ UnknownError
An unknown error occurred while converting the script.
@ InvalidScriptIncompatible
Script is incompatible with conversion to a QGIS expression.
QgsEsriArcadeConverter(const QString &script)
Constructor for QgsEsriArcadeConverter for converting the specified script.
QgsEsriArcadeExpression * expression() const
Returns the underlying expression.
QList< QgsEsriArcadeExpression * > arguments() const
Returns the list of argument expressions.
QString functionName() const
Returns the function name.
QgsEsriArcadeExpression * condition() const
Returns the condition expression.
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.
QgsEsriArcadeExpression * indexExpression() const
Returns the index expression.
QgsEsriArcadeExpression * containerExpression() const
Returns the container expression being indexed.
QVariant value() const
Returns the literal value.
QgsEsriArcadeExpression * containerExpression() const
Returns the container expression being accessed.
QString memberName() const
Returns the member attribute name.
QString name() const
Returns the profile variable name, including the leading $ symbol.
Visitor implementation that recursively traverses all child nodes in an Esri Arcade script.
void visit(const QgsEsriArcadeBlockStatement &node) override
Visit a QgsEsriArcadeBlockStatement node.
QgsEsriArcadeExpression * expression() const
Returns the expression returned by the statement, or nullptr if void return.
Parses and encapsulates an entire Esri Arcade script.
void acceptVisitor(QgsEsriArcadeVisitor &visitor) const
Accepts a visitor to traverse the script abstract syntax tree.
bool hasParserError() const
Returns true if a parsing error occurred.
QgsEsriArcadeExpression * operand() const
Returns the operand expression.
QString text() const
Returns text representation of the operator.
UnaryOperator unaryOperator() const
Returns the unary operator.
@ Minus
Minus operator, -.
QString name() const
Returns the variable identifier name.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes).
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
Custom exception class which is raised when an operation is not supported.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.