Оценка динамического логического выражения
Мне нужно оценить динамическое логическое выражение, и я знаю, что в ABAP это невозможно. Я нашел класс cl_java_script, и с помощью этого класса я смог выполнить свое требование. Я пробовал что-то вроде этого:
result = cl_java_script=>create( )->evaluate( `( 1 + 2 + 3 ) == 6 ;` ).
После того, как метод оценит результат выполнения = true, как и ожидалось. Но мое счастье закончилось, когда я заглянул в документацию по классу, в которой говорится, что этот класс устарел.
Мой вопрос в том, есть ли другой способ добиться этого?
1 ответ
Использование любого полного по Тьюрингу языка для разбора «динамического логического выражения» — ужасная идея, поскольку злоумышленник может запустить любую программу внутри вашего выражения, т.е.
while(true) { }
приведет к сбою вашего варианта, используя . Кроме того, хотя я не знаю подробностей
cl_java_script
, я предполагаю, что он запускает отдельную среду выполнения JS в отдельном потоке где-то, это не кажется самым эффективным выбором для вычисления такого небольшого динамического выражения.
Вместо этого вы можете реализовать свой собственный небольшой парсер. Это имеет то преимущество, что вы можете ограничить то, что он поддерживает, до минимума, в то же время имея возможность расширить его на все, что вам нужно в вашем случае использования. Вот небольшой пример с использованием нотации обратной полировки, который может правильно оценить выражение, которое вы показали (использование RPN значительно упрощает синтаксический анализ, хотя наверняка можно также создать полноценный анализатор выражений):
REPORT z_expr_parser.
TYPES:
BEGIN OF multi_value,
integer TYPE REF TO i,
boolean TYPE REF TO bool,
END OF multi_value.
CLASS lcl_rpn_parser DEFINITION.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING
text TYPE string,
parse
RETURNING VALUE(result) TYPE multi_value.
PRIVATE SECTION.
DATA:
tokens TYPE STANDARD TABLE OF string,
stack TYPE STANDARD TABLE OF multi_value.
METHODS pop_int
RETURNING VALUE(result) TYPE i.
METHODS pop_bool
RETURNING VALUE(result) TYPE abap_bool.
ENDCLASS.
CLASS lcl_rpn_parser IMPLEMENTATION.
METHOD constructor.
" a most simple lexer:
SPLIT text AT ' ' INTO TABLE tokens.
ASSERT lines( tokens ) > 0.
ENDMETHOD.
METHOD pop_int.
DATA(peek) = stack[ lines( stack ) ].
ASSERT peek-integer IS BOUND.
result = peek-integer->*.
DELETE stack INDEX lines( stack ).
ENDMETHOD.
METHOD pop_bool.
DATA(peek) = stack[ lines( stack ) ].
ASSERT peek-boolean IS BOUND.
result = peek-boolean->*.
DELETE stack INDEX lines( stack ).
ENDMETHOD.
METHOD parse.
LOOP AT tokens INTO DATA(token).
IF token = '='.
DATA(comparison) = xsdbool( pop_int( ) = pop_int( ) ).
APPEND VALUE #( boolean = NEW #( comparison ) ) TO stack.
ELSEIF token = '+'.
DATA(addition) = pop_int( ) + pop_int( ).
APPEND VALUE #( integer = NEW #( addition ) ) TO stack.
ELSE.
" assumption: token is integer
DATA value TYPE i.
value = token.
APPEND VALUE #( integer = NEW #( value ) ) TO stack.
ENDIF.
ENDLOOP.
ASSERT lines( stack ) = 1.
result = stack[ 1 ].
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
" 1 + 2 + 3 = 6 in RPN:
DATA(program) = |1 2 3 + + 6 =|.
DATA(parser) = NEW lcl_rpn_parser( program ).
DATA(result) = parser->parse( ).
ASSERT result-boolean IS BOUND.
ASSERT result-boolean->* = abap_true.