I need to evaluate a dynamic logical expression and I know that in ABAP it is not possible.
I found the class cl_java_script and with this class I could achieve my requeriment. I've try something like this:
result = cl_java_script=>create( )->evaluate( `( 1 + 2 + 3 ) == 6 ;` ).
After the method evaluate execution result = true as espected. But my happiness is over when I look into the class documentation that says This class is obsolete.
My question is, there is another way to achieve this?
Using any turing complete language to parse a "dynamic logical expression" is a terrible idea, as an attacker might be able to run any program inside your expression, i.e. while(true) { } will crash your variant using cl_java_script. Also although I don't know the details of cl_java_script, I assume it launches a separate JS runtime in a separate thread somewhere, this does not seem to be the most efficient choice to calculate such a small dynamic expression.
Instead you could implement your own small parser. This has the advantage that you can limit what it supports to the bare minimum whilst being able to extend it to everything you need in your usecase. Here's a small example using reverse polish notation which is able to correctly evaluate the expression you've shown (using RPN simplifies parsing a lot, though for sure one can also build a full fledged expression parser):
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.
SAPs BRF is an option, but potentially massive overkill in your scenario.
Here is a blog on calling BRF from abap.
And here is how Rules/Expressions can be defined dynamically.
BUT, if you know enough about the source problem to generate
1 + 2 + 3 = 6
Then it is hard to imagine why a simple custom parser cant be used.
Just how complex should the expressions be ?
Id probably write my own parser before investing in calling BRF.
Since some/many BSPs use server side JAVAscript and not ABAP as the scripting language, i cant see SAP removing the Kernel routine anytime soon.
SYSTEM-CALL JAVA SCRIPT EVALUATE.
SO maybe consider Just calling the cl_java_script anyway until it is an issue.
Then worry about a parser if and when it is really no longer a valid call.
But definitely some movement in the obsolete space here.
SAP is pushing/forcing you to cloud with the SDK, to execute such things.
https://sap.github.io/cloud-sdk/docs/js/overview-cloud-sdk-for-javascript
Related
I'm trying to port code from DML 1.2 to DML 1.4. Here is part of code that i ported:
group rx_queue [i < NQUEUES] {
<...>
param desctype = i < 64 #? regs.SRRCTL12[i].DESCTYPE.val #: regs.SRRCTL2[i - 64].DESCTYPE.val; // error occurs here
<...>
}
Error says:
error: non-constant expression: cast(i, int64 ) < 64
How can i specify parameter dependent on index value?
I tried to use if...else instead ternary operator, but it says that conditional parameters are not allowed in DML.
Index parameters in DML are a slightly magical expressions; when used from within parameters, they can evaluate to either a constant or a variable depending on where the parameter is used from. Consider the following example:
group g[i < 5] {
param x = i * 4;
method m() {
log info: "%d", x;
log info: "%d", g[4 - i].x;
log info: "%d", g[2].x;
}
}
i becomes an implicit local variable within the m method, and in params, indices are a bit like implicit macro parameters. When the compiler encounters x in the first log statement, the param will expand to i * 4 right away. In the second log statement, the x param is taken from an object indexed with the expression 4 - i, so param expansion will instead insert (5 - i) * 4. In the third log statement, the x param is taken from a constant indexed object, so it expands to 2 * 4 which collapses into the constant 8.
Most uses of desctype will likely happen from contexts where indices are variable, and the #? expression requires a constant boolean as condition, so this will likely give an error as soon as anyone tries to use it.
I would normally advise you to switch from #? to ? in the definition of the desctype param, but that fails in this particular case: DMLC will report error: array index out of bounds on the i - 64 expression. This error is much more confusing, and happens because DMLC automatically evaluates every parameter once with all zero indices, to smoke out misspelled identifiers; this will include evaluation of SRRCTL2[i-64] which collapses into SRRCTL2[-64] which annoys DMLC.
This is arguably a compiler bug; DMLC should probably be more tolerant in this corner. (Note that even if we would remove the zero-indexed validation step from the compiler, your parameter would still give the same error message if it ever would be explicitly referenced with a constant index, like log info: "%d", rx_queue[0].desctype).
The reason why you didn't get an error in DML 1.2 is that DML 1.2 had a single ternary operator ? that unified 1.4's ? and #?; when evaluated with a constant condition the dead branch would be disregarded without checking for errors. This had some strange effects in other situations, but made your particular use case work.
My concrete advise would be to replace the param with a method; this makes all index variables unconditionally non-constant which avoids the problem:
method desctype() -> (uint64) {
return i < 64 ? regs.SRRCTL12[i].DESCTYPE.val : regs.SRRCTL2[i - 64].DESCTYPE.val;
}
Why do I see
proc simple(a, b: int) : int =
result = a + b
so often in nim code when it seems as if
proc simple(a, b: int) : int =
a + b
would suffice? Is there any semantic difference between those two that I'm missing?
The only reference to implicitly returning the last statement I found on nim-lang wasn't in the manual but in the tut where it states that
[...] a proc's body can consist of a single expression whose value is
then returned implicitly.
Which seems misleading: it seems as it works for every 'last expression' (unless result was already set, then the result of the statement has to be discarded)
In the coding conventions (https://nim-lang.org/docs/nep1.html#introduction-coding-conventions) they recommend to use return only if it's needed in the flow.
The book Nim in Action says 'it's not idiomatic to use the return keyword as the last statement of the proc', but it's not explicit about result = a + b vs a + b. From the snippets around the book, the convention seems to be:
Prefer a + b.
Use result = a + b only if you are modifying result, as in result.add(b).
Use return a only to do an early exit from the proc.
The book also list this gotcha that won't compile:
proc resultVar: string =
result = "The result"
"This cause an error"
The reason behind code like result = a + b or return a is that people can't get all the idiomatics, specially when they are beginners like me. I still see for i in range(len(variable)) in Python code, which is not only non-pythonic but ugly and underperformant.
One of the more exotic features is the implicit result variable: every procedure in Nim with a non-void return type has an implicit result variable that represents the value that will be returned [Wikipedia].
My first stab at this problem was in C
#define 2 5
assert(2+2 == 10);
Unfortunately
error: macro name must be an identifier
I also tried Scheme
(define 2 5)
but
can't define a non-symbol: (define 2 5)
I was wondering if there are any programming languages where this is possible.
I sincerely hope not.
But that said, I do know of a way in Java: you can use reflection to adjust the values of the cached boxed Integers: a JVM must cache all values in the range -128 to +127, and there does exist a mechanism to adjust the numeric values in that cache!
See https://codegolf.stackexchange.com/questions/28786/write-a-program-that-makes-2-2-5/28818#28818 for more details. Here's the code in full:
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[132] = array[133];
System.out.printf("%d", 2 + 2);
}
}
The output is 5, essentially achieved by redefining the number 4.
I remembered hearing a story about this long ago, and today I decided to follow-up on it. That follow-up lead me to this question which unfortunately isn't exactly answered. This isn't exactly an answer, either, but ... here ya go.
I believe this can be done with early Fortran compilers.
From that post (which is a copy of an original post to comp.lang.python which I haven't been able to track-down):
> subroutine munge (i)
> i = 3
> return
>and then in your main program did:
> j = 7
> call munge (7)
> write (6, 11) j
> 11 format ('j = ', i6)
>it would print 3!
The reason seems to be due to a combination of:
Constant interning
Fortran's pass-by-reference semantics
The compiler interns the constant value (7) and then calls the munge function with a pointer to the interned "constant" 7. The munge function changes the value of the passed-by-reference integer value and so now the interned constant value is 3.
So, this won't universally change the value of 7 to 3 across the whole program, but anywhere that interned "constant" value is used will now be using 3 as its numeric value.
This can be extraordinarily surprising (and rightly so, IMO) to younger programmers who have only dealt with languages whose arguments are almost always passed by value (unless otherwise specified). C, C++, C#, Erlang, Java, javascript, Perl, Python, Ruby, etc. all use pass-by-value. For those languages, pass-by-reference semantics are achieved either using encapsulation (e.g. passing a mutable complex type such as an object) or explicitly passing a pointer to a value instead of the value (e.g. using the & operator in C and similar languages).
Xapian docs talk about a query constructor that takes a term position parameter, to be used in phrase searches:
Quote:
This constructor actually takes a couple of extra parameters, which
may be used to specify positional and frequency information for terms
in the query:
Xapian::Query(const string & tname_,
Xapian::termcount wqf_ = 1,
Xapian::termpos term_pos_ = 0)
The term_pos represents the position of the term in the query. Again,
this isn't useful for a single term query by itself, but is used for
phrase searching, passage retrieval, and other operations which
require knowledge of the order of terms in the query (such as
returning the set of matching terms in a given document in the same
order as they occur in the query). If such operations are not
required, the default value of 0 may be used.
And in the reference, we have:
Xapian::Query::Query ( const std::string & tname_,
Xapian::termcount wqf_ = 1,
Xapian::termpos pos_ = 0
)
A query consisting of a single term.
And:
typedef unsigned termpos
A term position within a document or query.
So, say I want to build a query for the phrase: "foo bar baz", how do I go about it?!
Does term_pos_ provide relative position values, ie define the order of terms within the document:
(I'm using here the python bindings API, as I'm more familiar with it)
q = xapian.Query(xapian.Query.OP_AND, [xapian.Query("foo", wqf, 1),xapian.Query("bar", wqf,2),xapian.Query("baz", wqf,3)] )
And just for the sake of testing, suppose we did:
q = xapian.Query(xapian.Query.OP_AND, [xapian.Query("foo", wqf, 3),xapian.Query("bar", wqf, 4),xapian.Query("baz", wqf, 5)] )
So this would give the same results as the previous example?!
And suppose we have:
q = xapian.Query(xapian.Query.OP_AND, [xapian.Query("foo", wqf, 2),xapian.Query("bar", wqf, 4),xapian.Query("baz", wqf, 5)] )
So now this would match where documents have "foo" "bar" separated with one term, followed by "baz" ??
Is it as such, or is it that this parameter is referring to absolute positions of the indexed terms?!
Edit:
And how is OP_PHRASE related to this? I find some online samples using OP_PHRASE as such:
q = xapian.Query(xapian.Query.OP_PHRASE, term_list)
This makes obvious sense, but then what is the role of the said term_pos_ constructor in phrase searches - is it a more surgical way of doing things!?
int pos = 1;
std::list<Xapian::Query> subs;
subs.push_back(Xapian::Query("foo", 1, pos++));
subs.push_back(Xapian::Query("bar", 1, pos++));
querylist.push_back(Xapian::Query(Xapian::Query::OP_PHRASE, subs.begin(), subs.end()));
There are some other questions on here that are similar but sufficiently different that I need to pose this as a fresh question:
I have created an empty class, lets call it Test. It doesn't have any properties or methods. I then iterate through a map of key/value pairs, dynamically creating properties named for the key and containing the value... like so:
def langMap = [:]
langMap.put("Zero",0)
langMap.put("One",1)
langMap.put("Two",2)
langMap.put("Three",3)
langMap.put("Four",4)
langMap.put("Five",5)
langMap.put("Six",6)
langMap.put("Seven",7)
langMap.put("Eight",8)
langMap.put("Nine",9)
langMap.each { key,val ->
Test.metaClass."${key}" = val
}
Now I can access these from a new method created like this:
Test.metaClass.twoPlusThree = { return Two + Three }
println test.twoPlusThree()
What I would like to do though, is dynamically load a set of instructions from a String, like "Two + Three", create a method on the fly to evaluate the result, and then iteratively repeat this process for however many strings containing expressions that I happen to have.
Questions:
a) First off, is there simply a better and more elegant way to do this (Based on the info I have given) ?
b) Assuming this path is viable, what is the syntax to dynamically construct this closure from a string, where the string references variable names valid only within a method on this class?
Thanks!
I think the correct answer depends on what you're actually trying to do. Can the input string be a more complicated expression, like '(Two + Six) / Four'?
If you want to allow more complex expressions, you may want to directly evaluate the string as a Groovy expression. Inside the GroovyConsole or a Groovy script, you can directly call evaluate, which will evaluate an expression in the context of that script:
def numNames = 'Zero One Two Three Four Five Six Seven Eight Nine'.split()
// Add each numer name as a property to the script.
numNames.eachWithIndex { name, i ->
this[name] = i
}
println evaluate('(Two + Six) / Four') // -> 2
If you are not in one of those script-friendly worlds, you can use the GroovyShell class:
def numNames = 'Zero One Two Three Four Five Six Seven Eight Nine'.split()
def langMap = [:]
numNames.eachWithIndex { name, i -> langMap[name] = i }
def shell = new GroovyShell(langMap as Binding)
println shell.evaluate('(Two + Six) / Four') // -> 2
But, be aware that using eval is very risky. If the input string is user-generated, i would not recommend you going this way; the user could input something like "rm -rf /".execute(), and, depending on the privileges of the script, erase everything from wherever that script is executed. You may first validate that the input string is "safe" (maybe checking it only contains known operators, whitespaces, parentheses and number names) but i don't know if that's safe enough.
Another alternative is defining your own mini-language for those expressions and then parsing them using something like ANTLR. But, again, this really depends on what you're trying to accomplish.