1 //===-- lib/Evaluate/fold-character.cpp -----------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "fold-implementation.h" 10 #include "fold-reduction.h" 11 12 namespace Fortran::evaluate { 13 14 static std::optional<ConstantSubscript> GetConstantLength( 15 FoldingContext &context, Expr<SomeType> &&expr) { 16 expr = Fold(context, std::move(expr)); 17 if (auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(expr)}) { 18 if (auto len{chExpr->LEN()}) { 19 return ToInt64(*len); 20 } 21 } 22 return std::nullopt; 23 } 24 25 template <typename T> 26 static std::optional<ConstantSubscript> GetConstantLength( 27 FoldingContext &context, FunctionRef<T> &funcRef, int zeroBasedArg) { 28 if (auto *expr{funcRef.UnwrapArgExpr(zeroBasedArg)}) { 29 return GetConstantLength(context, std::move(*expr)); 30 } else { 31 return std::nullopt; 32 } 33 } 34 35 template <typename T> 36 static std::optional<Scalar<T>> Identity( 37 Scalar<T> str, std::optional<ConstantSubscript> len) { 38 if (len) { 39 return CharacterUtils<T::kind>::REPEAT( 40 str, std::max<ConstantSubscript>(*len, 0)); 41 } else { 42 return std::nullopt; 43 } 44 } 45 46 template <int KIND> 47 Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction( 48 FoldingContext &context, 49 FunctionRef<Type<TypeCategory::Character, KIND>> &&funcRef) { 50 using T = Type<TypeCategory::Character, KIND>; 51 using StringType = Scalar<T>; // std::string or larger 52 using SingleCharType = typename StringType::value_type; // char &c. 53 auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 54 CHECK(intrinsic); 55 std::string name{intrinsic->name}; 56 if (name == "achar" || name == "char") { 57 using IntT = SubscriptInteger; 58 return FoldElementalIntrinsic<T, IntT>(context, std::move(funcRef), 59 ScalarFunc<T, IntT>([](const Scalar<IntT> &i) { 60 return CharacterUtils<KIND>::CHAR(i.ToUInt64()); 61 })); 62 } else if (name == "adjustl") { 63 return FoldElementalIntrinsic<T, T>( 64 context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTL); 65 } else if (name == "adjustr") { 66 return FoldElementalIntrinsic<T, T>( 67 context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTR); 68 } else if (name == "max") { 69 return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater); 70 } else if (name == "maxval") { 71 SingleCharType least{0}; 72 if (auto identity{Identity<T>( 73 StringType{least}, GetConstantLength(context, funcRef, 0))}) { 74 return FoldMaxvalMinval<T>( 75 context, std::move(funcRef), RelationalOperator::GT, *identity); 76 } 77 } else if (name == "merge") { 78 return FoldMerge<T>(context, std::move(funcRef)); 79 } else if (name == "min") { 80 return FoldMINorMAX(context, std::move(funcRef), Ordering::Less); 81 } else if (name == "minval") { 82 auto most{std::numeric_limits<SingleCharType>::max()}; 83 if (auto identity{Identity<T>( 84 StringType{most}, GetConstantLength(context, funcRef, 0))}) { 85 return FoldMaxvalMinval<T>( 86 context, std::move(funcRef), RelationalOperator::LT, *identity); 87 } 88 } else if (name == "new_line") { 89 return Expr<T>{Constant<T>{CharacterUtils<KIND>::NEW_LINE()}}; 90 } else if (name == "repeat") { // not elemental 91 if (auto scalars{GetScalarConstantArguments<T, SubscriptInteger>( 92 context, funcRef.arguments())}) { 93 return Expr<T>{Constant<T>{ 94 CharacterUtils<KIND>::REPEAT(std::get<Scalar<T>>(*scalars), 95 std::get<Scalar<SubscriptInteger>>(*scalars).ToInt64())}}; 96 } 97 } else if (name == "trim") { // not elemental 98 if (auto scalar{ 99 GetScalarConstantArguments<T>(context, funcRef.arguments())}) { 100 return Expr<T>{Constant<T>{ 101 CharacterUtils<KIND>::TRIM(std::get<Scalar<T>>(*scalar))}}; 102 } 103 } 104 // TODO: cshift, eoshift, maxloc, minloc, pack, reduce, 105 // spread, transfer, transpose, unpack 106 return Expr<T>{std::move(funcRef)}; 107 } 108 109 template <int KIND> 110 Expr<Type<TypeCategory::Character, KIND>> FoldOperation( 111 FoldingContext &context, Concat<KIND> &&x) { 112 if (auto array{ApplyElementwise(context, x)}) { 113 return *array; 114 } 115 using Result = Type<TypeCategory::Character, KIND>; 116 if (auto folded{OperandsAreConstants(x)}) { 117 return Expr<Result>{Constant<Result>{folded->first + folded->second}}; 118 } 119 return Expr<Result>{std::move(x)}; 120 } 121 122 template <int KIND> 123 Expr<Type<TypeCategory::Character, KIND>> FoldOperation( 124 FoldingContext &context, SetLength<KIND> &&x) { 125 if (auto array{ApplyElementwise(context, x)}) { 126 return *array; 127 } 128 using Result = Type<TypeCategory::Character, KIND>; 129 if (auto folded{OperandsAreConstants(x)}) { 130 auto oldLength{static_cast<ConstantSubscript>(folded->first.size())}; 131 auto newLength{folded->second.ToInt64()}; 132 if (newLength < oldLength) { 133 folded->first.erase(newLength); 134 } else { 135 folded->first.append(newLength - oldLength, ' '); 136 } 137 CHECK(static_cast<ConstantSubscript>(folded->first.size()) == newLength); 138 return Expr<Result>{Constant<Result>{std::move(folded->first)}}; 139 } 140 return Expr<Result>{std::move(x)}; 141 } 142 143 FOR_EACH_CHARACTER_KIND(template class ExpressionBase, ) 144 template class ExpressionBase<SomeCharacter>; 145 } // namespace Fortran::evaluate 146