B4A Library MteEval - B4X Expression Compiler and Eval Library (Open Source)

B4X Expression Compiler and Eval Library

MteEVAL is a library for compiling and evaluating expressions at runtime. Expressions are converted to bytecode and then executed on demand with a simple virtual machine.

There are five editions of the library:
  • Android (B4A)
  • iOS (B4i)
  • Java (B4J)
  • JavaS2 (B4A/B4J)
  • .NET (C#)
JavaS2 is our stage 2 performance edition of the library written in native Java.

Application

Creating expressions at runtime is a powerful tool allowing calculations and program flow to be modified after installation, which otherwise would require a physical update or a custom build of an application. For example, any application designed to manage a sales compensation plan could benefit from runtime expressions, where the end-user may want to customize the plan's formulas by team members, product mixes and sales goals.

Codeblocks

MteEVAL implements a single class named Codeblock. MteEval's codeblock adopts the expression syntax from the venerable 1990's xBase compiler Clipper 5 where the construct began. Codeblocks start with an open brace, followed by an optional parameter list between pipes, then the expression, and end with a closing brace.

{|<parameters>|<expression>}

Operator support

The library supports C/Java style operators along side a growing list of B4X native functions.
  • Math operators: +-*/%
  • Relational: > < >= <= != ==
  • Logical: || && !
  • Bitwise: << >> & ^ |
  • Assignment: =
  • Constants: cPI, cE
  • Functions: abs(), ceil(), floor(), iif(), if(), min(), max(), sqrt(), power(),round()
  • Trig Functions: acos(), acosd(), asin(), asind(), atan(), atand(), cos(), cosd(), sin(), sind(), tan(), tand()
Examples

You only need to compile a Codeblock once. Once compiled you can evaluate it as many times as needed, all while supplying different arguments. All arguments and the return value are type Double.

Example 1: Codeblock without parameters
B4X:
Dim cb as Codeblock
Dim Result as Double
cb.Initialize
cb.Compile( "{||5 + 3}" )
Result = cb.Eval           'Result=8

Example 2: Codeblock with parameters
B4X:
Dim cb as Codeblock
Dim Area as Double
cb.Initialize
cb.Compile( "{|length,width|length*width}" )
Area = cb.Eval2( Array( 3, 17 ) )    'Area=51

When evaluating with parameters, use the Eval2 method.

Example 3: Compile, eval and repeat
B4X:
Dim cb as Codeblock
Dim Commission1, Commission2, Commission3 As Double
cb.Initialize
cb.Compile( "{|sales,r1,r2| r1*sales + iif( sales > 100000, (sales-100000)*r2, 0 ) }" )
Commission1 = cb.Eval2( Array( 152000, .08, .05 ) )    'Commission1=14760
Commission2 = cb.Eval2( Array( 186100, .08, .07 ) )    'Commission2=20915
Commission3 = cb.Eval2( Array( 320000, .08, .05 ) )    'Commission3=36600

Linking to your project
  • To use the Android or Java editions, add the .JAR and .XML files to your Additional Libraries folder and check the MteEVAL library in the Libraries Manager of the IDE.
  • For iOS, copy the modules Codeblock.bas, Codegen.bas, PCODE.bas, and Run.bas to your project folder or place them in the Shared Modules folder. Then add the modules to the project through the IDE.
Demo

Library and demo attached.

Source

Source code is maintained here:
https://github.com/macthomasengineering

Follow this link to track the status of the library at Waffle.io
https://waffle.io/macthomasengineering/mteeval-b4x-library
 

Attachments

  • mteevalS2_b4a_v105.zip
    40.5 KB · Views: 384
  • mteeval_b4a_v106.zip
    46 KB · Views: 389
Last edited:

stanmiller

Active Member
Licensed User
Longtime User
What's New

1.06 (2017/01/30)
- Added "S2" performance improvements to B4X editions of the library including peephole optimization and separating bytecode and constant data into fixed length arrays.
- Renumbered Pcodes and grouped instructions in Run.bas to improve B4X Select/Case performance.
- Removed erroneous pop instruction from bitwise "not"
- Those library users not modifying the code should use the JavaS2 v1.05 edition for the best performance in B4A and B4J.

1.05 (2016/12/31)
** All features in this update are specific to the JavaS2 edition. The B4X editions are still at v1.04 and will be updated to the JavaS2 feature set in v1.06 **

- Introduced "Stage2" performance edition of library (B4A/B4J) with native Java port
- Added peephole optimization for PUSH, LOADVAR, and LOADCONST
- Optimizer can be disabled by setting Codeblock.DisableOptimizations=True
- Moved constants from inline to dedicated constants table.
- Optimized bytecode into a fixed length array of ints where before code was structured as a list of doubles.
- Removed erroneous pop instruction from bitwise "not"

1.04 (2016/10/10)
- Added trig functions: acos(), acosd(), asin(), asind(), atan(), atand(), cos(), cosd(), sin(), sind(), tan(), tand()
- Added ceil(), floor(), and round().
- Added support for variable assignments in expressions.

1.03 (2016/08/30)

- Added support for bitwise operators << >> ~ ^ ! &
- Added internal function Power()
- Removed Push instructions in DoIIF. This caused the error in the Kitchen Sink test case.

1.02 (2016/08/26)
- Fixed syntax error with parenthetical expression lists
- Moved software CPU to local stack to support Codeblock nesting
- Added support for hexadecimal number format 0xNNNN
- Added syntax error trap for bitwise ops until supported.
- Added strucural code to support bitwise operators in next release.

1.01 (2016/08/23)
- First public release
 
Last edited:

Daniel-White

Active Member
Licensed User
Longtime User
I beg your pardon, where we can use this, in which scenarios? , I am not sure to understand it o_O
 

stanmiller

Active Member
Licensed User
Longtime User
Great work

Thanks!

While there are practical applications to the library, there is also interesting computer science at work in the source. From the recursive descent parser, to the sequencing of instructions (particularly iif() which implements a full if/then/else scenario inline with short-cutting), to how the bytecode is executed through a simulated software CPU.

These virtual machines are especially powerful in embedded systems where you can multi-task the bytecode through pseudo processes, assigning each process a separate task as an alternative to modeling behavior through a complex state-machine. In addition you can guard against memory overwrites, stack overflows and corruption (in the domain of VM), and have the ability to swap out the code real-time without any system downtime.
 
Last edited:

wonder

Expert
Licensed User
Longtime User
Congratulations on the release! :)

One problem, though: Null pointer exception.
B4X:
cb.Compile("{|r,g,b|((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff)}")
 

stanmiller

Active Member
Licensed User
Longtime User
Congratulations on the release! :)

One problem, though: Null pointer exception.
B4X:
cb.Compile("{|r,g,b|((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff)}")

We haven't added support for the bitwise operators just yet. But that expression still shouldn't generate an exception. I've added it to the board at Waffle.
 
Last edited:

imbault

Well-Known Member
Licensed User
Longtime User
Anyway, Codeblocks on Clipper was one of the very smart and clever features of the V5 release Nantucket Clipper

A fresh, inovative and a very powerfull feature
 
Last edited:

stanmiller

Active Member
Licensed User
Longtime User
Congratulations on the release! :)

One problem, though: Null pointer exception.
B4X:
cb.Compile("{|r,g,b|((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff)}")

Library v1.03 posted. Bitwise ops now supported. Here's a decompile of the above code.

1_mteeval_bitwise_op_zpsaucinzf5.jpg
 
Last edited:

stanmiller

Active Member
Licensed User
Longtime User
Anyway, Codeblocks on Clipper was one of the very smart and clever features of the V5 release Nantucket Clipper

A fresh, inovative and a very powerfull feature

Indeed. Clipper's codeblock is an elegant little construct.

Also, if you're using the library in a project make sure to update to v1.03. IIF() was broken in the earlier builds. Straight tests of IIF() were ok. But when used in a compound expressions results would get skewed.
 
Last edited:

stanmiller

Active Member
Licensed User
Longtime User
I beg your pardon, where we can use this, in which scenarios? , I am not sure to understand it o_O

Hi Daniel, I added an Application section to the opening post which gives a typical use case. In short, compiling expressions at runtime allows an app user to modify app formulas and tailor them to their specific needs.
 
Last edited:

JackKirk

Well-Known Member
Licensed User
Longtime User
This is brilliant and I have an immediate use in my new app.

One small concern - either a typo on your part or a lack of understanding on mine:

Example 1: Codeblock without parameters
B4X:
Dim cb as Codeblock
cb.Initialize
cb.Compile( "{||5 * 3}" )
Result = cb.Eval           'Result=8

Should the result be 15 or should the expression be "5 + 3" or am I missing something?

Again, brilliant...
 

JackKirk

Well-Known Member
Licensed User
Longtime User
Sorry, digging around after downloading 1.03, I can't find the iOS files:

For iOS, copy the modules Codeblock.bas, Codegen.bas, PCODE.bas, and Run.bas to your project folder or place them in the Shared Modules folder. Then add the modules to the project through the IDE.

Apologies if I have stuffed up...
 

stanmiller

Active Member
Licensed User
Longtime User
This is brilliant and I have an immediate use in my new app.

One small concern - either a typo on your part or a lack of understanding on mine:



Should the result be 15 or should the expression be "5 + 3" or am I missing something?

Again, brilliant...

Thank you for the kind words!

That expression is most certainly a typo. It should be "5+3". Post fixed.
 
Top