Browse Source

fix(variform): allow numbers to be numbers

zmstone 1 year ago
parent
commit
e6330dddec

+ 5 - 3
apps/emqx_utils/src/emqx_variform.erl

@@ -83,8 +83,10 @@ eval_as_string(Expr, Bindings, _Opts) ->
 
 
 eval({str, Str}, _Bindings) ->
 eval({str, Str}, _Bindings) ->
     str(Str);
     str(Str);
-eval({num, Num}, _Bindings) ->
-    str(Num);
+eval({integer, Num}, _Bindings) ->
+    Num;
+eval({float, Num}, _Bindings) ->
+    Num;
 eval({array, Args}, Bindings) ->
 eval({array, Args}, Bindings) ->
     eval(Args, Bindings);
     eval(Args, Bindings);
 eval({call, FuncNameStr, Args}, Bindings) ->
 eval({call, FuncNameStr, Args}, Bindings) ->
@@ -150,7 +152,7 @@ resolve_func_name(FuncNameStr) ->
 resolve_var_value(VarName, Bindings) ->
 resolve_var_value(VarName, Bindings) ->
     case emqx_template:lookup_var(split(VarName), Bindings) of
     case emqx_template:lookup_var(split(VarName), Bindings) of
         {ok, Value} ->
         {ok, Value} ->
-            str(Value);
+            Value;
         {error, _Reason} ->
         {error, _Reason} ->
             <<>>
             <<>>
     end.
     end.

+ 4 - 2
apps/emqx_utils/src/emqx_variform_parser.yrl

@@ -7,7 +7,8 @@ Nonterminals
 
 
 Terminals
 Terminals
     identifier
     identifier
-    number
+    integer
+    float
     string
     string
     '(' ')'
     '(' ')'
     ',' '[' ']'.
     ',' '[' ']'.
@@ -34,7 +35,8 @@ args -> args ',' arg : '$1' ++ ['$3'].
 %% Arguments can be expressions, arrays, numbers, or strings
 %% Arguments can be expressions, arrays, numbers, or strings
 arg -> expr : '$1'.
 arg -> expr : '$1'.
 arg -> array : '$1'.
 arg -> array : '$1'.
-arg -> number : {num, element(3, '$1')}.
+arg -> integer: {integer, element(3, '$1')}.
+arg -> float: {float, element(3, '$1')}.
 arg -> string : {str, element(3, '$1')}.
 arg -> string : {str, element(3, '$1')}.
 
 
 Erlang code.
 Erlang code.

+ 4 - 3
apps/emqx_utils/src/emqx_variform_scan.xrl

@@ -3,7 +3,8 @@ Definitions.
 IDENTIFIER  = [a-zA-Z][a-zA-Z0-9_.]*
 IDENTIFIER  = [a-zA-Z][a-zA-Z0-9_.]*
 SQ_STRING   = \'[^\']*\'
 SQ_STRING   = \'[^\']*\'
 DQ_STRING   = \"[^\"]*\"
 DQ_STRING   = \"[^\"]*\"
-NUMBER      = [+-]?(\\d+\\.\\d+|[0-9]+)
+INTEGER     = [+-]?[0-9]+
+FLOAT       = [+-]?\\d+\\.\\d+
 LPAREN      = \(
 LPAREN      = \(
 RPAREN      = \)
 RPAREN      = \)
 LBRACKET    = \[
 LBRACKET    = \[
@@ -12,12 +13,12 @@ COMMA       = ,
 WHITESPACE  = [\s\t\n]+
 WHITESPACE  = [\s\t\n]+
 
 
 Rules.
 Rules.
-%% Match function names, variable names (with ${}), strings, numbers, and structural characters
 {WHITESPACE} : skip_token.
 {WHITESPACE} : skip_token.
 {IDENTIFIER} : {token, {identifier, TokenLine, TokenChars}}.
 {IDENTIFIER} : {token, {identifier, TokenLine, TokenChars}}.
 {SQ_STRING}  : {token, {string, TokenLine, unquote(TokenChars, $')}}.
 {SQ_STRING}  : {token, {string, TokenLine, unquote(TokenChars, $')}}.
 {DQ_STRING}  : {token, {string, TokenLine, unquote(TokenChars, $")}}.
 {DQ_STRING}  : {token, {string, TokenLine, unquote(TokenChars, $")}}.
-{NUMBER}     : {token, {number, TokenLine, TokenChars}}.
+{INTEGER}    : {token, {integer, TokenLine, list_to_integer(TokenChars)}}.
+{FLOAT}      : {token, {float, TokenLine, list_to_float(TokenChars)}}.
 {LPAREN}     : {token, {'(', TokenLine}}.
 {LPAREN}     : {token, {'(', TokenLine}}.
 {RPAREN}     : {token, {')', TokenLine}}.
 {RPAREN}     : {token, {')', TokenLine}}.
 {LBRACKET}   : {token, {'[', TokenLine}}.
 {LBRACKET}   : {token, {'[', TokenLine}}.

+ 33 - 0
apps/emqx_utils/test/emqx_variform_tests.erl

@@ -39,11 +39,44 @@ redner_test_() ->
         {"out of range nth index", fun() ->
         {"out of range nth index", fun() ->
             ?assertEqual({ok, <<>>}, render("nth(2, tokens(var, ','))", #{var => <<"a">>}))
             ?assertEqual({ok, <<>>}, render("nth(2, tokens(var, ','))", #{var => <<"a">>}))
         end},
         end},
+        {"string for nth index", fun() ->
+            ?assertEqual({ok, <<"a">>}, render("nth('1', tokens(var, ','))", #{var => <<"a">>}))
+        end},
         {"not a index number for nth", fun() ->
         {"not a index number for nth", fun() ->
             ?assertMatch(
             ?assertMatch(
                 {error, #{reason := invalid_argument, func := nth, index := <<"notnum">>}},
                 {error, #{reason := invalid_argument, func := nth, index := <<"notnum">>}},
                 render("nth('notnum', tokens(var, ','))", #{var => <<"a">>})
                 render("nth('notnum', tokens(var, ','))", #{var => <<"a">>})
             )
             )
+        end},
+        {"substr", fun() ->
+            ?assertMatch(
+                {ok, <<"b">>},
+                render("substr(var,1)", #{var => <<"ab">>})
+            )
+        end},
+        {"result in integer", fun() ->
+            ?assertMatch(
+                {ok, <<"2">>},
+                render("strlen(var)", #{var => <<"ab">>})
+            )
+        end},
+        {"result in float", fun() ->
+            ?assertMatch(
+                {ok, <<"2.2">>},
+                render("var", #{var => 2.2})
+            )
+        end},
+        {"concat a number", fun() ->
+            ?assertMatch(
+                {ok, <<"2.2">>},
+                render("concat(strlen(var),'.2')", #{var => <<"xy">>})
+            )
+        end},
+        {"var is an array", fun() ->
+            ?assertMatch(
+                {ok, <<"y">>},
+                render("nth(2,var)", #{var => [<<"x">>, <<"y">>]})
+            )
         end}
         end}
     ].
     ].