yson2_udf.cpp 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204
  1. #include <yql/essentials/minikql/dom/node.h>
  2. #include <yql/essentials/minikql/dom/json.h>
  3. #include <yql/essentials/minikql/dom/yson.h>
  4. #include <yql/essentials/minikql/dom/make.h>
  5. #include <yql/essentials/minikql/dom/peel.h>
  6. #include <yql/essentials/minikql/dom/hash.h>
  7. #include <yql/essentials/minikql/dom/convert.h>
  8. #include <yql/essentials/public/udf/udf_helpers.h>
  9. #include <yql/essentials/public/udf/udf_type_printer.h>
  10. #include <library/cpp/yson_pull/exceptions.h>
  11. #include <util/string/split.h>
  12. using namespace NYql::NUdf;
  13. using namespace NYql::NDom;
  14. using namespace NYsonPull;
  15. namespace {
  16. constexpr char OptionsResourceName[] = "Yson2.Options";
  17. using TOptionsResource = TResource<OptionsResourceName>;
  18. using TNodeResource = TResource<NodeResourceName>;
  19. using TDictType = TDict<char*, TNodeResource>;
  20. using TInt64DictType = TDict<char*, i64>;
  21. using TUint64DictType = TDict<char*, ui64>;
  22. using TBoolDictType = TDict<char*, bool>;
  23. using TDoubleDictType = TDict<char*, double>;
  24. using TStringDictType = TDict<char*, char*>;
  25. enum class EOptions : ui8 {
  26. Strict = 1,
  27. AutoConvert = 2
  28. };
  29. union TOpts {
  30. ui8 Raw = 0;
  31. struct {
  32. bool Strict: 1;
  33. bool AutoConvert: 1;
  34. };
  35. };
  36. static_assert(sizeof(TOpts) == 1U, "Wrong TOpts size.");
  37. TOpts ParseOptions(TUnboxedValuePod x) {
  38. if (x) {
  39. return TOpts{x.Get<ui8>()};
  40. }
  41. return {};
  42. }
  43. class TOptions : public TBoxedValue {
  44. TUnboxedValue Run(const IValueBuilder*, const TUnboxedValuePod* args) const override {
  45. ui8 options = 0;
  46. if (args[0] && args[0].Get<bool>()) {
  47. options |= ui8(EOptions::AutoConvert);
  48. }
  49. if (args[1] && args[1].Get<bool>()) {
  50. options |= ui8(EOptions::Strict);
  51. }
  52. return TUnboxedValuePod(options);
  53. }
  54. public:
  55. static const TStringRef& Name() {
  56. static auto name = TStringRef::Of("Options");
  57. return name;
  58. }
  59. static bool DeclareSignature(
  60. const TStringRef& name,
  61. TType* userType,
  62. IFunctionTypeInfoBuilder& builder,
  63. bool typesOnly) {
  64. Y_UNUSED(userType);
  65. if (Name() == name) {
  66. auto argsBuilder = builder.Args(2U);
  67. argsBuilder->Add<TOptional<bool>>().Name(TStringRef::Of("AutoConvert"));
  68. argsBuilder->Add<TOptional<bool>>().Name(TStringRef::Of("Strict"));
  69. builder.Returns(builder.Resource(OptionsResourceName));
  70. builder.OptionalArgs(2U);
  71. if (!typesOnly) {
  72. builder.Implementation(new TOptions);
  73. }
  74. builder.IsStrict();
  75. return true;
  76. } else {
  77. return false;
  78. }
  79. }
  80. };
  81. using TConverterPtr = TUnboxedValuePod (*)(TUnboxedValuePod, const IValueBuilder*, const TSourcePosition& pos);
  82. template <TConverterPtr Converter>
  83. class TLazyConveterT : public TManagedBoxedValue {
  84. public:
  85. TLazyConveterT(TUnboxedValue&& original, const IValueBuilder* valueBuilder, const TSourcePosition& pos)
  86. : Original(std::move(original)), ValueBuilder(valueBuilder), Pos_(pos)
  87. {}
  88. private:
  89. template <bool NoSwap>
  90. class TIterator: public TManagedBoxedValue {
  91. public:
  92. TIterator(TUnboxedValue&& original, const IValueBuilder* valueBuilder, const TSourcePosition& pos)
  93. : Original(std::move(original)), ValueBuilder(valueBuilder), Pos_(pos)
  94. {}
  95. private:
  96. bool Skip() final {
  97. return Original.Skip();
  98. }
  99. bool Next(TUnboxedValue& value) final {
  100. if (Original.Next(value)) {
  101. if constexpr (!NoSwap) {
  102. value = Converter(value.Release(), ValueBuilder, Pos_);
  103. }
  104. return true;
  105. }
  106. return false;
  107. }
  108. bool NextPair(TUnboxedValue& key, TUnboxedValue& payload) final {
  109. if (Original.NextPair(key, payload)) {
  110. if constexpr (NoSwap) {
  111. payload = Converter(payload.Release(), ValueBuilder, Pos_);
  112. } else {
  113. key = Converter(key.Release(), ValueBuilder, Pos_);
  114. }
  115. return true;
  116. }
  117. return false;
  118. }
  119. const TUnboxedValue Original;
  120. const IValueBuilder *const ValueBuilder;
  121. const TSourcePosition Pos_;
  122. };
  123. ui64 GetDictLength() const final {
  124. return Original.GetDictLength();
  125. }
  126. ui64 GetListLength() const final {
  127. return Original.GetListLength();
  128. }
  129. bool HasFastListLength() const final {
  130. return Original.HasFastListLength();
  131. }
  132. bool HasDictItems() const final {
  133. return Original.HasDictItems();
  134. }
  135. bool HasListItems() const final {
  136. return Original.HasListItems();
  137. }
  138. TUnboxedValue GetListIterator() const final {
  139. return TUnboxedValuePod(new TIterator<false>(Original.GetListIterator(), ValueBuilder, Pos_));
  140. }
  141. TUnboxedValue GetDictIterator() const final {
  142. return TUnboxedValuePod(new TIterator<true>(Original.GetDictIterator(), ValueBuilder, Pos_));
  143. }
  144. TUnboxedValue GetKeysIterator() const final {
  145. return TUnboxedValuePod(new TIterator<true>(Original.GetKeysIterator(), ValueBuilder, Pos_));
  146. }
  147. TUnboxedValue GetPayloadsIterator() const override {
  148. return TUnboxedValuePod(new TIterator<false>(Original.GetPayloadsIterator(), ValueBuilder, Pos_));
  149. }
  150. bool Contains(const TUnboxedValuePod& key) const final {
  151. return Original.Contains(key);
  152. }
  153. TUnboxedValue Lookup(const TUnboxedValuePod& key) const final {
  154. if (auto lookup = Original.Lookup(key)) {
  155. return Converter(lookup.Release().GetOptionalValue(), ValueBuilder, Pos_).MakeOptional();
  156. }
  157. return {};
  158. }
  159. bool IsSortedDict() const final {
  160. return Original.IsSortedDict();
  161. }
  162. const TUnboxedValue Original;
  163. const IValueBuilder *const ValueBuilder;
  164. const TSourcePosition Pos_;
  165. };
  166. template<bool Strict, bool AutoConvert, TConverterPtr Converter = nullptr>
  167. TUnboxedValuePod ConvertToListImpl(TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  168. if (!x) {
  169. return valueBuilder->NewEmptyList().Release();
  170. }
  171. switch (GetNodeType(x)) {
  172. case ENodeType::List:
  173. if (!x.IsBoxed())
  174. break;
  175. if constexpr (Converter != nullptr) {
  176. if constexpr (Strict || AutoConvert) {
  177. return TUnboxedValuePod(new TLazyConveterT<Converter>(x, valueBuilder, pos));
  178. } else {
  179. TSmallVec<TUnboxedValue, TUnboxedValue::TAllocator> values;
  180. if (const auto elements = x.GetElements()) {
  181. const auto size = x.GetListLength();
  182. values.reserve(size);
  183. for (ui32 i = 0U; i < size; ++i) {
  184. if (auto converted = Converter(elements[i], valueBuilder, pos)) {
  185. values.emplace_back(std::move(converted));
  186. }
  187. }
  188. } else {
  189. const auto it = x.GetListIterator();
  190. for (TUnboxedValue v; it.Next(v);) {
  191. if (auto converted = Converter(v.Release(), valueBuilder, pos)) {
  192. values.emplace_back(std::move(converted));
  193. }
  194. }
  195. }
  196. if (values.empty()) {
  197. break;
  198. }
  199. return valueBuilder->NewList(values.data(), values.size()).Release();
  200. }
  201. }
  202. return x;
  203. case ENodeType::Attr:
  204. return ConvertToListImpl<Strict, AutoConvert, Converter>(x.GetVariantItem().Release(), valueBuilder, pos);
  205. default:
  206. if constexpr (Strict) {
  207. if (!IsNodeType<ENodeType::List>(x)) {
  208. UdfTerminate((TStringBuilder() << valueBuilder->WithCalleePosition(pos) << " Cannot parse list from " << TDebugPrinter(x)).c_str());
  209. }
  210. }
  211. }
  212. return valueBuilder->NewEmptyList().Release();
  213. }
  214. template<bool Strict, bool AutoConvert, TConverterPtr Converter = nullptr>
  215. TUnboxedValuePod ConvertToDictImpl(TUnboxedValuePod x, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  216. if (!x) {
  217. return valueBuilder->NewEmptyList().Release();
  218. }
  219. switch (GetNodeType(x)) {
  220. case ENodeType::Dict:
  221. if (!x.IsBoxed())
  222. break;
  223. if constexpr (Converter != nullptr) {
  224. if constexpr (Strict || AutoConvert) {
  225. return TUnboxedValuePod(new TLazyConveterT<Converter>(x, valueBuilder, pos));
  226. } else if (const auto size = x.GetDictLength()) {
  227. TSmallVec<TPair, TStdAllocatorForUdf<TPair>> pairs;
  228. pairs.reserve(size);
  229. const auto it = x.GetDictIterator();
  230. for (TUnboxedValue key, payload; it.NextPair(key, payload);) {
  231. if (auto converted = Converter(payload, valueBuilder, pos)) {
  232. pairs.emplace_back(std::move(key), std::move(converted));
  233. }
  234. }
  235. if (pairs.empty()) {
  236. break;
  237. }
  238. return TUnboxedValuePod(IBoxedValuePtr(new TMapNode(pairs.data(), pairs.size())));
  239. }
  240. }
  241. return x;
  242. case ENodeType::Attr:
  243. return ConvertToDictImpl<Strict, AutoConvert, Converter>(x.GetVariantItem().Release(), valueBuilder, pos);
  244. default:
  245. if constexpr (Strict) {
  246. if (!IsNodeType<ENodeType::Dict>(x)) {
  247. UdfTerminate((TStringBuilder() << valueBuilder->WithCalleePosition(pos) << " Cannot parse dict from " << TDebugPrinter(x)).c_str());
  248. }
  249. }
  250. }
  251. return valueBuilder->NewEmptyList().Release();
  252. }
  253. template <TConverterPtr Converter = nullptr>
  254. TUnboxedValuePod LookupImpl(TUnboxedValuePod dict, const TUnboxedValuePod key, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  255. switch (GetNodeType(dict)) {
  256. case ENodeType::Dict:
  257. if (dict.IsBoxed()) {
  258. if (auto payload = dict.Lookup(key)) {
  259. if constexpr (Converter != nullptr) {
  260. return Converter(payload.Release().GetOptionalValue(), valueBuilder, pos);
  261. }
  262. return payload.Release();
  263. }
  264. }
  265. return {};
  266. case ENodeType::List:
  267. if (dict.IsBoxed()) {
  268. if (const i32 size = dict.GetListLength()) {
  269. if (i32 index; TryFromString(key.AsStringRef(), index) && index < size && index >= -size) {
  270. if (index < 0)
  271. index += size;
  272. if constexpr (Converter != nullptr) {
  273. return Converter(dict.Lookup(TUnboxedValuePod(index)).Release(), valueBuilder, pos);
  274. }
  275. return dict.Lookup(TUnboxedValuePod(index)).Release();
  276. }
  277. }
  278. }
  279. return {};
  280. case ENodeType::Attr:
  281. return LookupImpl<Converter>(dict.GetVariantItem().Release(), key, valueBuilder, pos);
  282. default:
  283. return {};
  284. }
  285. }
  286. template <TConverterPtr Converter = nullptr>
  287. TUnboxedValuePod YPathImpl(TUnboxedValuePod dict, const TUnboxedValuePod key, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  288. const std::string_view path = key.AsStringRef();
  289. if (path.size() < 2U || path.front() != '/' || path.back() == '/') {
  290. UdfTerminate((::TStringBuilder() << valueBuilder->WithCalleePosition(pos) << " Invalid YPath: '" << path << "'.").data());
  291. }
  292. for (const auto s : StringSplitter(path.substr(path[1U] == '/' ? 2U : 1U)).Split('/')) {
  293. const bool attr = IsNodeType<ENodeType::Attr>(dict);
  294. if (const std::string_view subpath = s.Token(); subpath == "@") {
  295. if (attr)
  296. dict = SetNodeType<ENodeType::Dict>(dict);
  297. else
  298. return {};
  299. } else {
  300. if (attr) {
  301. dict = dict.GetVariantItem().Release();
  302. }
  303. const auto subkey = valueBuilder->SubString(key, std::distance(path.begin(), subpath.begin()), subpath.size());
  304. dict = LookupImpl<nullptr>(dict, subkey, valueBuilder, pos);
  305. }
  306. if (!dict) {
  307. return {};
  308. }
  309. }
  310. if constexpr (Converter != nullptr) {
  311. return Converter(dict, valueBuilder, pos);
  312. }
  313. return dict;
  314. }
  315. template<bool Strict, bool AutoConvert>
  316. TUnboxedValuePod ContainsImpl(TUnboxedValuePod dict, TUnboxedValuePod key, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  317. switch (GetNodeType(dict)) {
  318. case ENodeType::Attr:
  319. return ContainsImpl<Strict, AutoConvert>(dict.GetVariantItem().Release(), key, valueBuilder, pos);
  320. case ENodeType::Dict:
  321. if (dict.IsBoxed())
  322. return TUnboxedValuePod(dict.Contains(key));
  323. else
  324. return TUnboxedValuePod(false);
  325. case ENodeType::List:
  326. if (dict.IsBoxed()) {
  327. if (const i32 size = dict.GetListLength()) {
  328. if (i32 index; TryFromString(key.AsStringRef(), index)) {
  329. return TUnboxedValuePod(index < size && index >= -size);
  330. }
  331. }
  332. }
  333. return TUnboxedValuePod(false);
  334. default:
  335. if constexpr (Strict && !AutoConvert)
  336. UdfTerminate((TStringBuilder() << valueBuilder->WithCalleePosition(pos) << " Can't check contains on scalar " << TDebugPrinter(dict)).c_str());
  337. else
  338. return {};
  339. }
  340. }
  341. template<bool Strict, bool AutoConvert>
  342. TUnboxedValuePod GetLengthImpl(TUnboxedValuePod dict, const IValueBuilder* valueBuilder, const TSourcePosition& pos) {
  343. switch (GetNodeType(dict)) {
  344. case ENodeType::Attr:
  345. return GetLengthImpl<Strict, AutoConvert>(dict.GetVariantItem().Release(), valueBuilder, pos);
  346. case ENodeType::Dict:
  347. return TUnboxedValuePod(dict.IsBoxed() ? dict.GetDictLength() : ui64(0));
  348. case ENodeType::List:
  349. return TUnboxedValuePod(dict.IsBoxed() ? dict.GetListLength() : ui64(0));
  350. default:
  351. if constexpr (Strict && !AutoConvert)
  352. UdfTerminate((TStringBuilder() << valueBuilder->WithCalleePosition(pos) << " Can't get container length from scalar " << TDebugPrinter(dict)).c_str());
  353. else
  354. return {};
  355. }
  356. }
  357. }
  358. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToBool, TOptional<bool>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  359. if (const auto options = ParseOptions(args[1]); options.Strict)
  360. return (options.AutoConvert ? &ConvertToBool<true, true> : &ConvertToBool<true, false>)(args[0], valueBuilder, GetPos());
  361. else
  362. return (options.AutoConvert ? &ConvertToBool<false, true> : &ConvertToBool<false, false>)(args[0], valueBuilder, GetPos());
  363. }
  364. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToInt64, TOptional<i64>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  365. if (const auto options = ParseOptions(args[1]); options.Strict)
  366. return (options.AutoConvert ? &ConvertToIntegral<true, true, i64> : &ConvertToIntegral<true, false, i64>)(args[0], valueBuilder, GetPos());
  367. else
  368. return (options.AutoConvert ? &ConvertToIntegral<false, true, i64> : &ConvertToIntegral<false, false, i64>)(args[0], valueBuilder, GetPos());
  369. }
  370. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToUint64, TOptional<ui64>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  371. if (const auto options = ParseOptions(args[1]); options.Strict)
  372. return (options.AutoConvert ? &ConvertToIntegral<true, true, ui64> : &ConvertToIntegral<true, false, ui64>)(args[0], valueBuilder, GetPos());
  373. else
  374. return (options.AutoConvert ? &ConvertToIntegral<false, true, ui64> : &ConvertToIntegral<false, false, ui64>)(args[0], valueBuilder, GetPos());
  375. }
  376. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToDouble, TOptional<double>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  377. if (const auto options = ParseOptions(args[1]); options.Strict)
  378. return (options.AutoConvert ? &ConvertToFloat<true, true, double> : &ConvertToFloat<true, false, double>)(args[0], valueBuilder, GetPos());
  379. else
  380. return (options.AutoConvert ? &ConvertToFloat<false, true, double> : &ConvertToFloat<false, false, double>)(args[0], valueBuilder, GetPos());
  381. }
  382. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToString, TOptional<char*>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  383. if (const auto options = ParseOptions(args[1]); options.Strict)
  384. return (options.AutoConvert ? &ConvertToString<true, true, false> : &ConvertToString<true, false, false>)(args[0], valueBuilder, GetPos());
  385. else
  386. return (options.AutoConvert ? &ConvertToString<false, true, false> : &ConvertToString<false, false, false>)(args[0], valueBuilder, GetPos());
  387. }
  388. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToList, TListType<TNodeResource>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  389. if (const auto options = ParseOptions(args[1]); options.Strict)
  390. return (options.AutoConvert ? &ConvertToListImpl<true, true> : &ConvertToListImpl<true, false>)(args[0], valueBuilder, GetPos());
  391. else
  392. return (options.AutoConvert ? &ConvertToListImpl<false, true> : &ConvertToListImpl<false, false>)(args[0], valueBuilder, GetPos());
  393. }
  394. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToInt64List, TListType<i64>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  395. if (const auto options = ParseOptions(args[1]); options.Strict)
  396. return (options.AutoConvert ? &ConvertToListImpl<true, true, &ConvertToIntegral<true, true, i64>> : &ConvertToListImpl<true, false, &ConvertToIntegral<true, false, i64>>)(args[0], valueBuilder, GetPos());
  397. else
  398. return (options.AutoConvert ? &ConvertToListImpl<false, true, &ConvertToIntegral<false, true, i64>> : &ConvertToListImpl<false, false, &ConvertToIntegral<false, false, i64>>)(args[0], valueBuilder, GetPos());
  399. }
  400. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToUint64List, TListType<ui64>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  401. if (const auto options = ParseOptions(args[1]); options.Strict)
  402. return (options.AutoConvert ? &ConvertToListImpl<true, true, &ConvertToIntegral<true, true, ui64>> : &ConvertToListImpl<true, false, &ConvertToIntegral<true, false, ui64>>)(args[0], valueBuilder, GetPos());
  403. else
  404. return (options.AutoConvert ? &ConvertToListImpl<false, true, &ConvertToIntegral<false, true, ui64>> : &ConvertToListImpl<false, false, &ConvertToIntegral<false, false, ui64>>)(args[0], valueBuilder, GetPos());
  405. }
  406. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToBoolList, TListType<bool>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  407. if (const auto options = ParseOptions(args[1]); options.Strict)
  408. return (options.AutoConvert ? &ConvertToListImpl<true, true, &ConvertToBool<true, true>> : &ConvertToListImpl<true, false, &ConvertToBool<true, false>>)(args[0], valueBuilder, GetPos());
  409. else
  410. return (options.AutoConvert ? &ConvertToListImpl<false, true, &ConvertToBool<false, true>> : &ConvertToListImpl<false, false, &ConvertToBool<false, false>>)(args[0], valueBuilder, GetPos());
  411. }
  412. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToDoubleList, TListType<double>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  413. if (const auto options = ParseOptions(args[1]); options.Strict)
  414. return (options.AutoConvert ? &ConvertToListImpl<true, true, &ConvertToFloat<true, true, double>> : &ConvertToListImpl<true, false, &ConvertToFloat<true, false, double>>)(args[0], valueBuilder, GetPos());
  415. else
  416. return (options.AutoConvert ? &ConvertToListImpl<false, true, &ConvertToFloat<false, true, double>> : &ConvertToListImpl<false, false, &ConvertToFloat<false, false, double>>)(args[0], valueBuilder, GetPos());
  417. }
  418. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToStringList, TListType<char*>(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  419. if (const auto options = ParseOptions(args[1]); options.Strict)
  420. return (options.AutoConvert ? &ConvertToListImpl<true, true, &ConvertToString<true, true, false>> : &ConvertToListImpl<true, false, &ConvertToString<true, false, false>>)(args[0], valueBuilder, GetPos());
  421. else
  422. return (options.AutoConvert ? &ConvertToListImpl<false, true, &ConvertToString<false, true, false>> : &ConvertToListImpl<false, false, &ConvertToString<false, false, false>>)(args[0], valueBuilder, GetPos());
  423. }
  424. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToDict, TDictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  425. if (const auto options = ParseOptions(args[1]); options.Strict)
  426. return (options.AutoConvert ? &ConvertToDictImpl<true, true> : &ConvertToDictImpl<true, false>)(args[0], valueBuilder, GetPos());
  427. else
  428. return (options.AutoConvert ? &ConvertToDictImpl<false, true> : &ConvertToDictImpl<false, false>)(args[0], valueBuilder, GetPos());
  429. }
  430. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToInt64Dict, TInt64DictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  431. if (const auto options = ParseOptions(args[1]); options.Strict)
  432. return (options.AutoConvert ? &ConvertToDictImpl<true, true, &ConvertToIntegral<true, true, i64>> : &ConvertToDictImpl<true, false, &ConvertToIntegral<true, false, i64>>)(args[0], valueBuilder, GetPos());
  433. else
  434. return (options.AutoConvert ? &ConvertToDictImpl<false, true, &ConvertToIntegral<false, true, i64>> : &ConvertToDictImpl<false, false, &ConvertToIntegral<false, false, i64>>)(args[0], valueBuilder, GetPos());
  435. }
  436. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToUint64Dict, TUint64DictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  437. if (const auto options = ParseOptions(args[1]); options.Strict)
  438. return (options.AutoConvert ? &ConvertToDictImpl<true, true, &ConvertToIntegral<true, true, ui64>> : &ConvertToDictImpl<true, false, &ConvertToIntegral<true, false, ui64>>)(args[0], valueBuilder, GetPos());
  439. else
  440. return (options.AutoConvert ? &ConvertToDictImpl<false, true, &ConvertToIntegral<false, true, ui64>> : &ConvertToDictImpl<false, false, &ConvertToIntegral<false, false, ui64>>)(args[0], valueBuilder, GetPos());
  441. }
  442. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToBoolDict, TBoolDictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  443. if (const auto options = ParseOptions(args[1]); options.Strict)
  444. return (options.AutoConvert ? &ConvertToDictImpl<true, true, &ConvertToBool<true, true>> : &ConvertToDictImpl<true, false, &ConvertToBool<true, false>>)(args[0], valueBuilder, GetPos());
  445. else
  446. return (options.AutoConvert ? &ConvertToDictImpl<false, true, &ConvertToBool<false, true>> : &ConvertToDictImpl<false, false, &ConvertToBool<false, false>>)(args[0], valueBuilder, GetPos());
  447. }
  448. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToDoubleDict, TDoubleDictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  449. if (const auto options = ParseOptions(args[1]); options.Strict)
  450. return (options.AutoConvert ? &ConvertToDictImpl<true, true, &ConvertToFloat<true, true, double>> : &ConvertToDictImpl<true, false, &ConvertToFloat<true, false, double>>)(args[0], valueBuilder, GetPos());
  451. else
  452. return (options.AutoConvert ? &ConvertToDictImpl<false, true, &ConvertToFloat<false, true, double>> : &ConvertToDictImpl<false, false, &ConvertToFloat<false, false, double>>)(args[0], valueBuilder, GetPos());
  453. }
  454. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TConvertToStringDict, TStringDictType(TOptional<TNodeResource>, TOptional<TOptionsResource>), 1) {
  455. if (const auto options = ParseOptions(args[1]); options.Strict)
  456. return (options.AutoConvert ? &ConvertToDictImpl<true, true, &ConvertToString<true, true, false>> : &ConvertToDictImpl<true, false, &ConvertToString<true, false, false>>)(args[0], valueBuilder, GetPos());
  457. else
  458. return (options.AutoConvert ? &ConvertToDictImpl<false, true, &ConvertToString<false, true, false>> : &ConvertToDictImpl<false, false, &ConvertToString<false, false, false>>)(args[0], valueBuilder, GetPos());
  459. }
  460. SIMPLE_STRICT_UDF(TAttributes, TDictType(TAutoMap<TNodeResource>)) {
  461. const auto x = args[0];
  462. if (IsNodeType<ENodeType::Attr>(x)) {
  463. return x;
  464. }
  465. return valueBuilder->NewEmptyList();
  466. }
  467. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TContains, TOptional<bool>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  468. if (const auto options = ParseOptions(args[2]); options.Strict)
  469. return (options.AutoConvert ? &ContainsImpl<true, true> : &ContainsImpl<true, false>)(args[0], args[1], valueBuilder, GetPos());
  470. else
  471. return (options.AutoConvert ? &ContainsImpl<false, true> : &ContainsImpl<false, false>)(args[0], args[1], valueBuilder, GetPos());
  472. }
  473. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TGetLength, TOptional<ui64>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>), 1) {
  474. if (const auto options = ParseOptions(args[1]); options.Strict)
  475. return (options.AutoConvert ? &GetLengthImpl<true, true> : &GetLengthImpl<true, false>)(args[0], valueBuilder, GetPos());
  476. else
  477. return (options.AutoConvert ? &GetLengthImpl<false, true> : &GetLengthImpl<false, false>)(args[0], valueBuilder, GetPos());
  478. }
  479. SIMPLE_STRICT_UDF_WITH_OPTIONAL_ARGS(TLookup, TOptional<TNodeResource>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  480. return LookupImpl(args[0], args[1], valueBuilder, GetPos());
  481. }
  482. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupBool, TOptional<bool>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  483. if (const auto options = ParseOptions(args[2]); options.Strict)
  484. return (options.AutoConvert ? &LookupImpl<&ConvertToBool<true, true>> : &LookupImpl<&ConvertToBool<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  485. else
  486. return (options.AutoConvert ? &LookupImpl<&ConvertToBool<false, true>> : &LookupImpl<&ConvertToBool<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  487. }
  488. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupInt64, TOptional<i64>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  489. if (const auto options = ParseOptions(args[2]); options.Strict)
  490. return (options.AutoConvert ? &LookupImpl<&ConvertToIntegral<true, true, i64>> : &LookupImpl<&ConvertToIntegral<true, false, i64>>)(args[0], args[1], valueBuilder, GetPos());
  491. else
  492. return (options.AutoConvert ? &LookupImpl<&ConvertToIntegral<false, true, i64>> : &LookupImpl<&ConvertToIntegral<false, false, i64>>)(args[0], args[1], valueBuilder, GetPos());
  493. }
  494. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupUint64, TOptional<ui64>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  495. if (const auto options = ParseOptions(args[2]); options.Strict)
  496. return (options.AutoConvert ? &LookupImpl<&ConvertToIntegral<true, true, ui64>> : &LookupImpl<&ConvertToIntegral<true, false, ui64>>)(args[0], args[1], valueBuilder, GetPos());
  497. else
  498. return (options.AutoConvert ? &LookupImpl<&ConvertToIntegral<false, true, ui64>> : &LookupImpl<&ConvertToIntegral<false, false, ui64>>)(args[0], args[1], valueBuilder, GetPos());
  499. }
  500. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupDouble, TOptional<double>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  501. if (const auto options = ParseOptions(args[2]); options.Strict)
  502. return (options.AutoConvert ? &LookupImpl<&ConvertToFloat<true, true, double>> : &LookupImpl<&ConvertToFloat<true, false, double>>)(args[0], args[1], valueBuilder, GetPos());
  503. else
  504. return (options.AutoConvert ? &LookupImpl<&ConvertToFloat<false, true, double>> : &LookupImpl<&ConvertToFloat<false, false, double>>)(args[0], args[1], valueBuilder, GetPos());
  505. }
  506. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupString, TOptional<char*>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  507. if (const auto options = ParseOptions(args[2]); options.Strict)
  508. return (options.AutoConvert ? &LookupImpl<&ConvertToString<true, true, false>> : &LookupImpl<&ConvertToString<true, false, false>>)(args[0], args[1], valueBuilder, GetPos());
  509. else
  510. return (options.AutoConvert ? &LookupImpl<&ConvertToString<false, true, false>> : &LookupImpl<&ConvertToString<false, false, false>>)(args[0], args[1], valueBuilder, GetPos());
  511. }
  512. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupList, TOptional<TListType<TNodeResource>>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  513. if (const auto options = ParseOptions(args[2]); options.Strict)
  514. return (options.AutoConvert ? &LookupImpl<&ConvertToListImpl<true, true>> : &LookupImpl<&ConvertToListImpl<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  515. else
  516. return (options.AutoConvert ? &LookupImpl<&ConvertToListImpl<false, true>> : &LookupImpl<&ConvertToListImpl<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  517. }
  518. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TLookupDict, TOptional<TDictType>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  519. if (const auto options = ParseOptions(args[2]); options.Strict)
  520. return (options.AutoConvert ? &LookupImpl<&ConvertToDictImpl<true, true>> : &LookupImpl<&ConvertToDictImpl<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  521. else
  522. return (options.AutoConvert ? &LookupImpl<&ConvertToDictImpl<false, true>> : &LookupImpl<&ConvertToDictImpl<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  523. }
  524. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPath, TOptional<TNodeResource>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  525. return YPathImpl(args[0], args[1], valueBuilder, GetPos());
  526. }
  527. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathBool, TOptional<bool>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  528. if (const auto options = ParseOptions(args[2]); options.Strict)
  529. return (options.AutoConvert ? &YPathImpl<&ConvertToBool<true, true>> : &YPathImpl<&ConvertToBool<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  530. else
  531. return (options.AutoConvert ? &YPathImpl<&ConvertToBool<false, true>> : &YPathImpl<&ConvertToBool<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  532. }
  533. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathInt64, TOptional<i64>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  534. if (const auto options = ParseOptions(args[2]); options.Strict)
  535. return (options.AutoConvert ? &YPathImpl<&ConvertToIntegral<true, true, i64>> : &YPathImpl<&ConvertToIntegral<true, false, i64>>)(args[0], args[1], valueBuilder, GetPos());
  536. else
  537. return (options.AutoConvert ? &YPathImpl<&ConvertToIntegral<false, true, i64>> : &YPathImpl<&ConvertToIntegral<false, false, i64>>)(args[0], args[1], valueBuilder, GetPos());
  538. }
  539. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathUint64, TOptional<ui64>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  540. if (const auto options = ParseOptions(args[2]); options.Strict)
  541. return (options.AutoConvert ? &YPathImpl<&ConvertToIntegral<true, true, ui64>> : &YPathImpl<&ConvertToIntegral<true, false, ui64>>)(args[0], args[1], valueBuilder, GetPos());
  542. else
  543. return (options.AutoConvert ? &YPathImpl<&ConvertToIntegral<false, true, ui64>> : &YPathImpl<&ConvertToIntegral<false, false, ui64>>)(args[0], args[1], valueBuilder, GetPos());
  544. }
  545. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathDouble, TOptional<double>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  546. if (const auto options = ParseOptions(args[2]); options.Strict)
  547. return (options.AutoConvert ? &YPathImpl<&ConvertToFloat<true, true, double>> : &YPathImpl<&ConvertToFloat<true, false, double>>)(args[0], args[1], valueBuilder, GetPos());
  548. else
  549. return (options.AutoConvert ? &YPathImpl<&ConvertToFloat<false, true, double>> : &YPathImpl<&ConvertToFloat<false, false, double>>)(args[0], args[1], valueBuilder, GetPos());
  550. }
  551. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathString, TOptional<char*>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  552. if (const auto options = ParseOptions(args[2]); options.Strict)
  553. return (options.AutoConvert ? &YPathImpl<&ConvertToString<true, true, false>> : &YPathImpl<&ConvertToString<true, false, false>>)(args[0], args[1], valueBuilder, GetPos());
  554. else
  555. return (options.AutoConvert ? &YPathImpl<&ConvertToString<false, true, false>> : &YPathImpl<&ConvertToString<false, false, false>>)(args[0], args[1], valueBuilder, GetPos());
  556. }
  557. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathList, TOptional<TListType<TNodeResource>>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  558. if (const auto options = ParseOptions(args[2]); options.Strict)
  559. return (options.AutoConvert ? &YPathImpl<&ConvertToListImpl<true, true>> : &YPathImpl<&ConvertToListImpl<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  560. else
  561. return (options.AutoConvert ? &YPathImpl<&ConvertToListImpl<false, true>> : &YPathImpl<&ConvertToListImpl<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  562. }
  563. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TYPathDict, TOptional<TDictType>(TAutoMap<TNodeResource>, char*, TOptional<TOptionsResource>), 1) {
  564. if (const auto options = ParseOptions(args[2]); options.Strict)
  565. return (options.AutoConvert ? &YPathImpl<&ConvertToDictImpl<true, true>> : &YPathImpl<&ConvertToDictImpl<true, false>>)(args[0], args[1], valueBuilder, GetPos());
  566. else
  567. return (options.AutoConvert ? &YPathImpl<&ConvertToDictImpl<false, true>> : &YPathImpl<&ConvertToDictImpl<false, false>>)(args[0], args[1], valueBuilder, GetPos());
  568. }
  569. SIMPLE_STRICT_UDF(TSerialize, TYson(TAutoMap<TNodeResource>)) {
  570. return valueBuilder->NewString(SerializeYsonDomToBinary(args[0]));
  571. }
  572. SIMPLE_STRICT_UDF(TSerializeText, TYson(TAutoMap<TNodeResource>)) {
  573. return valueBuilder->NewString(SerializeYsonDomToText(args[0]));
  574. }
  575. SIMPLE_STRICT_UDF(TSerializePretty, TYson(TAutoMap<TNodeResource>)) {
  576. return valueBuilder->NewString(SerializeYsonDomToPrettyText(args[0]));
  577. }
  578. constexpr char SkipMapEntity[] = "SkipMapEntity";
  579. constexpr char EncodeUtf8[] = "EncodeUtf8";
  580. constexpr char WriteNanAsString[] = "WriteNanAsString";
  581. SIMPLE_UDF_WITH_OPTIONAL_ARGS(TSerializeJson, TOptional<TJson>(TAutoMap<TNodeResource>, TOptional<TOptionsResource>, TNamedArg<bool, SkipMapEntity>, TNamedArg<bool, EncodeUtf8>, TNamedArg<bool, WriteNanAsString>), 4) try {
  582. return valueBuilder->NewString(SerializeJsonDom(args[0], args[2].GetOrDefault(false), args[3].GetOrDefault(false), args[4].GetOrDefault(false)));
  583. } catch (const std::exception& e) {
  584. if (ParseOptions(args[1]).Strict) {
  585. UdfTerminate((::TStringBuilder() << valueBuilder->WithCalleePosition(GetPos()) << " " << e.what()).data());
  586. }
  587. return {};
  588. }
  589. SIMPLE_STRICT_UDF(TWithAttributes, TOptional<TNodeResource>(TAutoMap<TNodeResource>, TAutoMap<TNodeResource>)) {
  590. Y_UNUSED(valueBuilder);
  591. TUnboxedValue x = args[0];
  592. auto y = args[1];
  593. if (!IsNodeType<ENodeType::Dict>(y)) {
  594. return {};
  595. }
  596. if (IsNodeType<ENodeType::Attr>(x)) {
  597. x = x.GetVariantItem();
  598. }
  599. if (y.IsEmbedded()) {
  600. return x;
  601. }
  602. if (!y.IsBoxed()) {
  603. return {};
  604. }
  605. // clone dict as attrnode
  606. if (const auto resource = y.GetResource()) {
  607. return SetNodeType<ENodeType::Attr>(TUnboxedValuePod(new TAttrNode(std::move(x), static_cast<const TPair*>(resource), y.GetDictLength())));
  608. } else {
  609. TSmallVec<TPair, TStdAllocatorForUdf<TPair>> items;
  610. items.reserve(y.GetDictLength());
  611. const auto it = y.GetDictIterator();
  612. for (TUnboxedValue x, y; it.NextPair(x, y);) {
  613. items.emplace_back(std::move(x), std::move(y));
  614. }
  615. if (items.empty()) {
  616. return x;
  617. }
  618. return SetNodeType<ENodeType::Attr>(TUnboxedValuePod(new TAttrNode(std::move(x), items.data(), items.size())));
  619. }
  620. }
  621. template<ENodeType Type>
  622. TUnboxedValuePod IsTypeImpl(TUnboxedValuePod y) {
  623. if (IsNodeType<ENodeType::Attr>(y)) {
  624. y = y.GetVariantItem().Release();
  625. }
  626. return TUnboxedValuePod(IsNodeType<Type>(y));
  627. }
  628. SIMPLE_STRICT_UDF(TIsString, bool(TAutoMap<TNodeResource>)) {
  629. Y_UNUSED(valueBuilder);
  630. return IsTypeImpl<ENodeType::String>(*args);
  631. }
  632. SIMPLE_STRICT_UDF(TIsInt64, bool(TAutoMap<TNodeResource>)) {
  633. Y_UNUSED(valueBuilder);
  634. return IsTypeImpl<ENodeType::Int64>(*args);
  635. }
  636. SIMPLE_STRICT_UDF(TIsUint64, bool(TAutoMap<TNodeResource>)) {
  637. Y_UNUSED(valueBuilder);
  638. return IsTypeImpl<ENodeType::Uint64>(*args);
  639. }
  640. SIMPLE_STRICT_UDF(TIsBool, bool(TAutoMap<TNodeResource>)) {
  641. Y_UNUSED(valueBuilder);
  642. return IsTypeImpl<ENodeType::Bool>(*args);
  643. }
  644. SIMPLE_STRICT_UDF(TIsDouble, bool(TAutoMap<TNodeResource>)) {
  645. Y_UNUSED(valueBuilder);
  646. return IsTypeImpl<ENodeType::Double>(*args);
  647. }
  648. SIMPLE_STRICT_UDF(TIsList, bool(TAutoMap<TNodeResource>)) {
  649. Y_UNUSED(valueBuilder);
  650. return IsTypeImpl<ENodeType::List>(*args);
  651. }
  652. SIMPLE_STRICT_UDF(TIsDict, bool(TAutoMap<TNodeResource>)) {
  653. Y_UNUSED(valueBuilder);
  654. return IsTypeImpl<ENodeType::Dict>(*args);
  655. }
  656. SIMPLE_STRICT_UDF(TIsEntity, bool(TAutoMap<TNodeResource>)) {
  657. Y_UNUSED(valueBuilder);
  658. return IsTypeImpl<ENodeType::Entity>(*args);
  659. }
  660. SIMPLE_STRICT_UDF(TEquals, bool(TAutoMap<TNodeResource>, TAutoMap<TNodeResource>)) {
  661. Y_UNUSED(valueBuilder);
  662. return TUnboxedValuePod(EquateDoms(args[0], args[1]));
  663. }
  664. SIMPLE_STRICT_UDF(TGetHash, ui64(TAutoMap<TNodeResource>)) {
  665. Y_UNUSED(valueBuilder);
  666. return TUnboxedValuePod(HashDom(args[0]));
  667. }
  668. namespace {
  669. class TBase: public TBoxedValue {
  670. public:
  671. typedef bool TTypeAwareMarker;
  672. TBase(TSourcePosition pos, const ITypeInfoHelper::TPtr typeHelper, const TType* shape)
  673. : Pos_(pos), TypeHelper_(typeHelper), Shape_(shape)
  674. {}
  675. protected:
  676. template<bool MoreTypesAllowed>
  677. static const TType* CheckType(const ITypeInfoHelper::TPtr typeHelper, const TType* shape) {
  678. switch (const auto kind = typeHelper->GetTypeKind(shape)) {
  679. case ETypeKind::Null:
  680. case ETypeKind::Void:
  681. case ETypeKind::EmptyList:
  682. case ETypeKind::EmptyDict:
  683. return MoreTypesAllowed ? nullptr : shape;
  684. case ETypeKind::Data:
  685. switch (TDataTypeInspector(*typeHelper, shape).GetTypeId()) {
  686. case TDataType<char*>::Id:
  687. case TDataType<TUtf8>::Id:
  688. case TDataType<bool>::Id:
  689. case TDataType<i8>::Id:
  690. case TDataType<i16>::Id:
  691. case TDataType<i32>::Id:
  692. case TDataType<i64>::Id:
  693. case TDataType<ui8>::Id:
  694. case TDataType<ui16>::Id:
  695. case TDataType<ui32>::Id:
  696. case TDataType<ui64>::Id:
  697. case TDataType<float>::Id:
  698. case TDataType<double>::Id:
  699. case TDataType<TYson>::Id:
  700. case TDataType<TJson>::Id:
  701. return nullptr;
  702. default:
  703. return shape;
  704. }
  705. case ETypeKind::Optional:
  706. return CheckType<MoreTypesAllowed>(typeHelper, TOptionalTypeInspector(*typeHelper, shape).GetItemType());
  707. case ETypeKind::List:
  708. return CheckType<MoreTypesAllowed>(typeHelper, TListTypeInspector(*typeHelper, shape).GetItemType());
  709. case ETypeKind::Dict: {
  710. const auto dictTypeInspector = TDictTypeInspector(*typeHelper, shape);
  711. if (const auto keyType = dictTypeInspector.GetKeyType(); ETypeKind::Data == typeHelper->GetTypeKind(keyType))
  712. if (const auto keyId = TDataTypeInspector(*typeHelper, keyType).GetTypeId(); keyId == TDataType<char*>::Id || keyId == TDataType<TUtf8>::Id)
  713. return CheckType<MoreTypesAllowed>(typeHelper, dictTypeInspector.GetValueType());
  714. return shape;
  715. }
  716. case ETypeKind::Tuple:
  717. if (const auto tupleTypeInspector = TTupleTypeInspector(*typeHelper, shape); auto count = tupleTypeInspector.GetElementsCount()) do
  718. if (const auto bad = CheckType<MoreTypesAllowed>(typeHelper, tupleTypeInspector.GetElementType(--count)))
  719. return bad;
  720. while (count);
  721. return nullptr;
  722. case ETypeKind::Struct:
  723. if (const auto structTypeInspector = TStructTypeInspector(*typeHelper, shape); auto count = structTypeInspector.GetMembersCount()) do
  724. if (const auto bad = CheckType<MoreTypesAllowed>(typeHelper, structTypeInspector.GetMemberType(--count)))
  725. return bad;
  726. while (count);
  727. return nullptr;
  728. case ETypeKind::Variant:
  729. if constexpr (MoreTypesAllowed)
  730. return CheckType<MoreTypesAllowed>(typeHelper, TVariantTypeInspector(*typeHelper, shape).GetUnderlyingType());
  731. else
  732. return shape;
  733. case ETypeKind::Resource:
  734. if (const auto inspector = TResourceTypeInspector(*typeHelper, shape); TStringBuf(inspector.GetTag()) == NodeResourceName)
  735. return nullptr;
  736. [[fallthrough]]; // AUTOGENERATED_FALLTHROUGH_FIXME
  737. default:
  738. return shape;
  739. }
  740. }
  741. const TSourcePosition Pos_;
  742. const ITypeInfoHelper::TPtr TypeHelper_;
  743. const TType *const Shape_;
  744. };
  745. class TFrom: public TBase {
  746. TUnboxedValue Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const final {
  747. return MakeDom(TypeHelper_.Get(), Shape_, *args, valueBuilder);
  748. }
  749. public:
  750. static const TStringRef& Name() {
  751. static auto name = TStringRef::Of("From");
  752. return name;
  753. }
  754. TFrom(TSourcePosition pos, const ITypeInfoHelper::TPtr typeHelper, const TType* shape)
  755. : TBase(pos, typeHelper, shape)
  756. {}
  757. static bool DeclareSignature(const TStringRef& name, TType* userType, IFunctionTypeInfoBuilder& builder, bool typesOnly) {
  758. if (Name() == name) {
  759. if (!userType) {
  760. builder.SetError("Missing user type.");
  761. return true;
  762. }
  763. builder.UserType(userType);
  764. const auto typeHelper = builder.TypeInfoHelper();
  765. const auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
  766. if (!userTypeInspector || userTypeInspector.GetElementsCount() < 1) {
  767. builder.SetError("Invalid user type.");
  768. return true;
  769. }
  770. const auto argsTypeTuple = userTypeInspector.GetElementType(0);
  771. const auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
  772. if (!argsTypeInspector) {
  773. builder.SetError("Invalid user type - expected tuple.");
  774. return true;
  775. }
  776. if (argsTypeInspector.GetElementsCount() != 1) {
  777. builder.SetError("Expected single argument.");
  778. return true;
  779. }
  780. const auto inputType = argsTypeInspector.GetElementType(0);
  781. if (const auto badType = CheckType<true>(typeHelper, inputType)) {
  782. ::TStringBuilder sb;
  783. sb << "Impossible to create DOM from incompatible with Yson type: ";
  784. TTypePrinter(*typeHelper, inputType).Out(sb.Out);
  785. if (badType != inputType) {
  786. sb << " Incompatible type: ";
  787. TTypePrinter(*typeHelper, badType).Out(sb.Out);
  788. }
  789. builder.SetError(sb);
  790. return true;
  791. }
  792. builder.Args()->Add(inputType).Done().Returns(builder.Resource(NodeResourceName));
  793. if (!typesOnly) {
  794. builder.Implementation(new TFrom(builder.GetSourcePosition(), typeHelper, inputType));
  795. }
  796. builder.IsStrict();
  797. return true;
  798. } else {
  799. return false;
  800. }
  801. }
  802. };
  803. class TConvert: public TBase {
  804. TUnboxedValue Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const final {
  805. if (const auto options = ParseOptions(args[1]); options.Strict)
  806. return (options.AutoConvert ? &PeelDom<true, true> : &PeelDom<true, false>)(TypeHelper_.Get(), Shape_, args[0], valueBuilder, Pos_);
  807. else
  808. return (options.AutoConvert ? &PeelDom<false, true> : &PeelDom<false, false>)(TypeHelper_.Get(), Shape_, args[0], valueBuilder, Pos_);
  809. }
  810. public:
  811. TConvert(TSourcePosition pos, const ITypeInfoHelper::TPtr typeHelper, const TType* shape)
  812. : TBase(pos, typeHelper, shape)
  813. {}
  814. static const TStringRef& Name() {
  815. static auto name = TStringRef::Of("ConvertTo");
  816. return name;
  817. }
  818. static bool DeclareSignature(const TStringRef& name, TType* userType, IFunctionTypeInfoBuilder& builder, bool typesOnly) {
  819. if (Name() == name) {
  820. const auto optionsType = builder.Optional()->Item(builder.Resource(OptionsResourceName)).Build();
  821. builder.OptionalArgs(1);
  822. if (!userType) {
  823. builder.SetError("Missing user type.");
  824. return true;
  825. }
  826. builder.UserType(userType);
  827. const auto typeHelper = builder.TypeInfoHelper();
  828. const auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
  829. if (!userTypeInspector || userTypeInspector.GetElementsCount() < 3) {
  830. builder.SetError("Invalid user type.");
  831. return true;
  832. }
  833. const auto argsTypeTuple = userTypeInspector.GetElementType(0);
  834. const auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
  835. if (!argsTypeInspector) {
  836. builder.SetError("Invalid user type - expected tuple.");
  837. return true;
  838. }
  839. if (const auto argsCount = argsTypeInspector.GetElementsCount(); argsCount < 1 || argsCount > 2) {
  840. ::TStringBuilder sb;
  841. sb << "Invalid user type - expected one or two arguments, got: " << argsCount;
  842. builder.SetError(sb);
  843. return true;
  844. }
  845. const auto resultType = userTypeInspector.GetElementType(2);
  846. if (const auto badType = CheckType<false>(typeHelper, resultType)) {
  847. ::TStringBuilder sb;
  848. sb << "Impossible to convert DOM to incompatible with Yson type: ";
  849. TTypePrinter(*typeHelper, resultType).Out(sb.Out);
  850. if (badType != resultType) {
  851. sb << " Incompatible type: ";
  852. TTypePrinter(*typeHelper, badType).Out(sb.Out);
  853. }
  854. builder.SetError(sb);
  855. return true;
  856. }
  857. builder.Args()->Add(builder.Resource(NodeResourceName)).Flags(ICallablePayload::TArgumentFlags::AutoMap).Add(optionsType);
  858. builder.Returns(resultType);
  859. if (!typesOnly) {
  860. builder.Implementation(new TConvert(builder.GetSourcePosition(), typeHelper, resultType));
  861. }
  862. return true;
  863. } else {
  864. return false;
  865. }
  866. }
  867. };
  868. template<typename TYJson, bool DecodeUtf8 = false>
  869. class TParse: public TBoxedValue {
  870. public:
  871. typedef bool TTypeAwareMarker;
  872. private:
  873. const TSourcePosition Pos_;
  874. const bool StrictType_;
  875. TUnboxedValue Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const final;
  876. public:
  877. TParse(TSourcePosition pos, bool strictType)
  878. : Pos_(pos), StrictType_(strictType)
  879. {}
  880. static const TStringRef& Name();
  881. static bool DeclareSignature(const TStringRef& name, TType* userType, IFunctionTypeInfoBuilder& builder, bool typesOnly) {
  882. if (Name() == name) {
  883. auto typeId = TDataType<TYJson>::Id;
  884. if (userType) {
  885. const auto typeHelper = builder.TypeInfoHelper();
  886. const auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
  887. if (!userTypeInspector || userTypeInspector.GetElementsCount() < 1) {
  888. builder.SetError("Missing or invalid user type.");
  889. return true;
  890. }
  891. const auto argsTypeTuple = userTypeInspector.GetElementType(0);
  892. const auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
  893. if (!argsTypeInspector) {
  894. builder.SetError("Invalid user type - expected tuple.");
  895. return true;
  896. }
  897. const auto argsCount = argsTypeInspector.GetElementsCount();
  898. if (argsCount < 1 || argsCount > 2) {
  899. ::TStringBuilder sb;
  900. sb << "Invalid user type - expected one or two arguments, got: " << argsCount;
  901. builder.SetError(sb);
  902. return true;
  903. }
  904. const auto inputType = argsTypeInspector.GetElementType(0);
  905. auto dataType = inputType;
  906. if (const auto optInspector = TOptionalTypeInspector(*typeHelper, inputType)) {
  907. dataType = optInspector.GetItemType();
  908. }
  909. if (const auto resInspector = TResourceTypeInspector(*typeHelper, dataType)) {
  910. typeId = TDataType<TYJson>::Id;
  911. } else {
  912. const auto dataInspector = TDataTypeInspector(*typeHelper, dataType);
  913. typeId = dataInspector.GetTypeId();
  914. }
  915. builder.UserType(userType);
  916. }
  917. const auto optionsType = builder.Optional()->Item(builder.Resource(OptionsResourceName)).Build();
  918. builder.OptionalArgs(1);
  919. switch (typeId) {
  920. case TDataType<TYJson>::Id:
  921. builder.Args()->Add<TAutoMap<TYJson>>().Add(optionsType).Done().Returns(builder.Resource(NodeResourceName));
  922. builder.IsStrict();
  923. break;
  924. case TDataType<TUtf8>::Id:
  925. builder.Args()->Add<TAutoMap<TUtf8>>().Add(optionsType).Done().Returns(builder.Optional()->Item(builder.Resource(NodeResourceName)).Build());
  926. break;
  927. default:
  928. builder.Args()->Add<TAutoMap<char*>>().Add(optionsType).Done().Returns(builder.Optional()->Item(builder.Resource(NodeResourceName)).Build());
  929. break;
  930. }
  931. if (!typesOnly) {
  932. builder.Implementation(new TParse(builder.GetSourcePosition(), TDataType<TYJson>::Id == typeId));
  933. }
  934. return true;
  935. } else {
  936. return false;
  937. }
  938. }
  939. };
  940. template<>
  941. TUnboxedValue TParse<TYson, false>::Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const try {
  942. return TryParseYsonDom(args[0].AsStringRef(), valueBuilder);
  943. } catch (const std::exception& e) {
  944. if (StrictType_ || ParseOptions(args[1]).Strict) {
  945. UdfTerminate((::TStringBuilder() << valueBuilder->WithCalleePosition(Pos_) << " " << e.what()).data());
  946. }
  947. return TUnboxedValuePod();
  948. }
  949. template<>
  950. TUnboxedValue TParse<TJson, false>::Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const try {
  951. return TryParseJsonDom(args[0].AsStringRef(), valueBuilder);
  952. } catch (const std::exception& e) {
  953. if (StrictType_ || ParseOptions(args[1]).Strict) {
  954. UdfTerminate((::TStringBuilder() << valueBuilder->WithCalleePosition(Pos_) << " " << e.what()).data());
  955. }
  956. return TUnboxedValuePod();
  957. }
  958. template<>
  959. TUnboxedValue TParse<TJson, true>::Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const try {
  960. return TryParseJsonDom(args[0].AsStringRef(), valueBuilder, true);
  961. } catch (const std::exception& e) {
  962. if (StrictType_ || ParseOptions(args[1]).Strict) {
  963. UdfTerminate((::TStringBuilder() << valueBuilder->WithCalleePosition(Pos_) << " " << e.what()).data());
  964. }
  965. return TUnboxedValuePod();
  966. }
  967. template<>
  968. const TStringRef& TParse<TYson, false>::Name() {
  969. static auto yson = TStringRef::Of("Parse");
  970. return yson;
  971. }
  972. template<>
  973. const TStringRef& TParse<TJson, false>::Name() {
  974. static auto yson = TStringRef::Of("ParseJson");
  975. return yson;
  976. }
  977. template<>
  978. const TStringRef& TParse<TJson, true>::Name() {
  979. static auto yson = TStringRef::Of("ParseJsonDecodeUtf8");
  980. return yson;
  981. }
  982. }
  983. // TODO: optimizer that marks UDFs as strict if Yson::Options(false as Strict) is given
  984. SIMPLE_MODULE(TYson2Module,
  985. TOptions,
  986. TParse<TYson>,
  987. TParse<TJson>,
  988. TParse<TJson, true>,
  989. TConvert,
  990. TConvertToBool,
  991. TConvertToInt64,
  992. TConvertToUint64,
  993. TConvertToDouble,
  994. TConvertToString,
  995. TConvertToList,
  996. TConvertToBoolList,
  997. TConvertToInt64List,
  998. TConvertToUint64List,
  999. TConvertToDoubleList,
  1000. TConvertToStringList,
  1001. TConvertToDict,
  1002. TConvertToBoolDict,
  1003. TConvertToInt64Dict,
  1004. TConvertToUint64Dict,
  1005. TConvertToDoubleDict,
  1006. TConvertToStringDict,
  1007. TAttributes,
  1008. TContains,
  1009. TLookup,
  1010. TLookupBool,
  1011. TLookupInt64,
  1012. TLookupUint64,
  1013. TLookupDouble,
  1014. TLookupString,
  1015. TLookupList,
  1016. TLookupDict,
  1017. TYPath,
  1018. TYPathBool,
  1019. TYPathInt64,
  1020. TYPathUint64,
  1021. TYPathDouble,
  1022. TYPathString,
  1023. TYPathList,
  1024. TYPathDict,
  1025. TSerialize,
  1026. TSerializeText,
  1027. TSerializePretty,
  1028. TSerializeJson,
  1029. TWithAttributes,
  1030. TIsString,
  1031. TIsInt64,
  1032. TIsUint64,
  1033. TIsBool,
  1034. TIsDouble,
  1035. TIsList,
  1036. TIsDict,
  1037. TIsEntity,
  1038. TFrom,
  1039. TGetLength,
  1040. TEquals,
  1041. TGetHash
  1042. );
  1043. REGISTER_MODULES(TYson2Module);