Выражения сценариев

Обзор

Выражение – это заданная автором сценария последовательность арифметических или строковых действий, оперирующая константами, переменными и функциями от них, в том числе функциями с сайд-эффектами. Выражение используется для подстановки в качестве аргумента в значение компонентов сценария.

Результат выражения может иметь тип сроки или числа. Даты также представляются в виде строк в формате RFC3339.

Значения типа bool на выходе автоматически преобразуются к строке true или false. С помощью функции ifelse(bool,any,any) результат можно привести к другим значениям, например числам 0 или 1.

Чтобы в качестве аргумента выражения использовать значение переменной, следует указать ее имя в квадратных скобках.

Для явного указания строки следует заключать текст в кавычки. В противном случае, сначала будут вычислены все арифметические комбинации, доступные для расчета. В приведенных примерах 2.3 и 2.4 результат будет разный, то есть началом результирующей строки примера 2.3 будет являться символ «5», а примера 2.4 – символы «23».

Разбиение на строки производится с помощью функции endline().

Примеры числовых выражений:

  • (1.1) [переменная_число_1] + 1

  • (1.2) 2 ^ [переменная_число_2] * ( Log10 ( [переменная_число_3] ) + 2 )

  • (1.3) sin ( len ( [переменная_строка_1] ) )

Примеры строковых выражений:

  • (2.1) [переменная_строка_1] + [переменная_строка_2]

  • (2.2) "Кусок текста" + [переменная_строка_1]

  • (2.3) 2 + 3 + [переменная_строка_1]

  • (2.4) "2" + "3" + [переменная_строка_1]

  • (2.5) SubStr ( [переменная_строка], 1, Length ( [переменная_строка] ) - 1 )

  • (2.6) If ( num([a]) > 5, "больше", "меньше")

Операции

Table 1. Операции над аргументами
Функция Тип значения Описание

+

num | str

Если аргументы могут быть приведены к числам, то результатом является сумма двух чисел. Иначе аргументы они приводятся к строке и сцепляются.

++

str

Приводит аргументы к строке и сцепляет их.

-

num

Если аргументы могут быть приведены к числам, то результатом является их разность. Иначе операция завершается с ошибкой.

*

num

Если аргументы могут быть приведены к числам, то результатом является их произведение. Иначе операция завершается с ошибкой.

/

num

Если аргументы могут быть приведены к числам, то результатом является их частное. Иначе операция завершается с ошибкой.

div

int

Если аргументы могут быть приведены к целым числам, то результатом является целая часть их частного. Иначе операция завершается с ошибкой.

rem

int

Если аргументы могут быть приведены к целым числам, то результатом является остаток от деления. Иначе операция завершается с ошибкой.

==

bool

Приводит значения аргументов к одному типу и сравнивает их. Равенство.

/=

bool

Приводит значения аргументов к одному типу и сравнивает их. Неравенство.

>

bool

Приводит значения аргументов к одному типу и сравнивает их. Больше.

>=

bool

Приводит значения аргументов к одному типу и сравнивает их. Больше или равно.

<

bool

Приводит значения аргументов к одному типу и сравнивает их. Меньше.

=<

bool

Приводит значения аргументов к одному типу и сравнивает их. Меньше или равно.

Экранирование JSON

При работе с JSON, собирая строковое значение, необходимо использовать конкатенацию строк и экранирование двойных кавычек.

Пример:
"{
   \"a\": null,
   \"b\": 1,
   \"c\": \"value\",
   \"d\": false,
   \"e\": \"" + str([name]) + "\",
   \"f\": \"" + str(num([a]) + num([res]) + 10) + "\",
   \"g\": [
     \"g1\", \"g2\", \"g3\"
   ],
   \"h\": {
     \"h1\": 1,
     \"h2\": \"value\",
     \"h3\": [1, 2, 3],
     \"h4\": {
       \"h41\": \"" + str(nowtick()) + "\",
       \"h42\": {\"x\": \"value\"}
     }
   }
}"

В ряде случаев удобнее собирать JSON в виде шаблона - компактнее и меньше символов экранирования. При этом из выражения в шаблон и обратно можно в любой момент переключиться, если текущее содержание поля корректно.

Шаблоны

Приложение редактора сценариев позволяет задавать выражения с помощью шаблонов.

Шаблон представляет собой строку, в которую в фигурных скобках входят вычислимые выражения. При этом переход между вкладками задания шаблона и задания выражения производит автоматическое взаимное преобразование.

Пример выражения
"abc." + str([var]) + ".def" + endline() + replace([var],";",".") + substring([var2],3,5) + "zzz"
Пример шаблона
abc.{str([var])}.def
{replace([var1],";",".") + substring([var2],3,5)}zzz

В шаблонах, содержащих фигурные скобки в самих значениях (например, JSON), следует их экранировать с помощью обратного слэша.

Пример:
\{
   "a": null,
   "b": 1,
   "c": "value",
   "d": false,
   "e": "{str([name])}",
   "f": "{str(num([a]) + num([res]) + 10)}",
   "g": [
     "g1", "g2", "g3"
   ],
   "h": \{
     "h1": 1,
     "h2": "value",
     "h3": [1, 2, 3],
     "h4": \{
       "h41": "{str(nowtick())}",
       "h42": \{"x": "value"\}
     \}
   \}
\}

Синтаксис erlang

Выражения интерпретируются в качестве кода на языке erlang. Перед интерпретацией производится предварительная трансляция для поддержки встроенных функций, подстановки переменных.

Поддерживаются функции высоких порядков, вызовы системных модулей, а также модулей-плагинов, добавленных в систему с помощью патчей.

ВАЖНО: значения в одинарных кавычках задают атомы (кроме случаев, когда одинарные кавычки используются в теле строк, бинарных выражений или комментариев). Атомы - это неудаляемые константы вплоть до перезапуска ноды. Поэтому при формировании строк лучше использовать двойные кавычки.

Решая сложную алгоритмическую задачу в сценарии, предполагающую чистую функцию, может быть полезным переведение ее в код выражения на erlang. Ниже приводится пример выражения на erlang.

Пример
%
io:format("DEBUG. a~n",[]),
    Ctx = jsx:decode([context],[return_maps]),
io:format("DEBUG. b~n",[]),
    P = jsx:decode([param],[return_maps]),
io:format("DEBUG. c~n",[]),
%
    Map2 = case P of
         Map when is_map(Map) -> maps:merge(Ctx,Map);
         [<<"merge">>,Map] when is_map(Map) -> r_env_utils:merge_maps(Map,Ctx,2);
         [<<"increment">>,Fld] when is_binary(Fld) ->
            Ctx#{Fld => r_env_utils:to_int(maps:get(Fld,Ctx,0),0) + 1};
         [<<"increment">>,Map] when is_map(Map) ->
            io:format("increment ~120tp~n",[Map]),
            lists:foldl(fun(Fld,Acc) ->
                                Acc#{Fld => r_env_utils:to_int(maps:get(Fld,Acc,0),0) + 1}
                        end, Ctx, maps:keys(Map));
         [<<"append">>,Map] when is_map(Map) ->
             Map1 = r_env_utils:merge_maps(Map,Ctx,2),
             maps:fold(fun(K,V,Acc) ->
                                 case maps:get(K,Ctx,undefined) of
                                       [_|_]=L when is_list(V) -> Acc#{K => L++V};
                                       _ -> Acc
                                 end end, Map1, Map)
    end,
io:format("DEBUG. d~n",[]),
%
    jsx:encode(Map2, [{space,2},{indent,1}])

Конкретно этот пример осуществляет функцию изменения контекста в переменной 'context' на основе управляющей команды из переменной 'param'. Формат значений того и другого установлен самим сценарием и поддерживается в выражении.

Выражение использует модули самой платформы erlang (maps, io), модуль подключенной зависимости по работе с JSON (jsx), модуль коммуникацинной платформы (r_env_utils), а также функции основного модуля erlang (is_list).

Выражение исполняясь выводит в консоль ноды отладочную информацию.