B2Program
This is the code generator B2Program for generating code from B to other programming languages.
A subset of B is supported for Java and C++ now. The work for code generation for Python, Clojure and C has begun but not continued.
Paper: https://www.researchgate.net/publication/337441241_A_Multi-target_Code_Generator_for_High-Level_B
Citation:
@inbook{inbook,
author = {Vu, Fabian and Hansen, Dominik and K{\"{o}}rner, Philipp and Leuschel, Michael},
year = {2019},
month = {11},
pages = {456-473},
title = {A Multi-target Code Generator for High-Level B},
isbn = {978-3-030-34967-7},
doi = {10.1007/978-3-030-34968-4_25}
}
Note:
- The implementation of the B types in C++ uses persistent set from: https://github.com/arximboldi/immer
- The library must first be installed before the generated C++ code can be used.
- The generated code for C works for a subset of the generated code that works for Java and C++.
- Sets and couples are not supported for C. Including other machines is not supported in C, too. The only types that are implemented for C are BInteger and BBoolean. An example where code generation for C works is the machine Lift.
- Executing all tests requires building Java B Types and installing C++ B Types
Build Java B Types
Primitive Integer: make btypes_primitives
Big Integer: make btypes_big_integer
Install C++ B Types
mkdir build & cd build
cmake ..
make install
Make sure that the immer library and gmpxx library (for big integers) are installed before.
Supported Subset of B
Machine sections:
Machine Section | Usage |
---|---|
SETS | S (Deferred Set) |
T = {e1, e2, ...} (Enumerated Set) | |
CONSTANTS | x,y, ... |
CONCRETE_CONSTANTS | cx, cy, ... |
PROPERTIES | c = v (where c is a constant and v is a value) |
card(S) = n (where S is a deferred set and n is a number) | |
S = {c1,...,cn} & card(S) = n (where S is a deferred set, c1,..., cn are constants and n is a number) | |
VARIABLES | x,y, ... |
CONCRETE_VARIABLES | cx, cy, ... |
INVARIANT | P (Logical Predicate) |
ASSERTIONS | P1;...;P2 (List Of Logical Predicates) |
INITIALISATION | |
OPERATIONS |
Note that code is not generated from INVARIANT and ASSERTIONS. These constructs are used for verifying the machine only. CONSTRAINTS and DEFINITIONS clause are not supported for code generation.
Machine inclusion:
Machine inclusion | Usage |
---|---|
INCLUDES | M1 ... MN (List of Machines) |
EXTENDS | M1 ... MN (List of Machines) |
Other machine inclusion clauses (SEES, USES, PROMOTES and REFINES) are not supported yet.
Logical Predicates:
Predicate | Meaning |
---|---|
P & Q | conjunction |
P or Q | disjunction |
P => Q | implication |
P <=> Q | equivalence |
not P | negation |
!(x1,...,xn).(P => Q) | universal quantification |
#(x1,...,xn).(P & Q) | existential quantification |
Restriction: As universal quantifications and existential quantifications are quantified constructs, the predicate P must constraint the value of the variables x1, ..., xn. P is a conjunction of n conjuncts where the i-th conjunct must constraint xi for each i in {1,...,n}.
Equality:
Predicate | Meaning |
---|---|
E = F | equality |
E = F | inequality |
Booleans:
Boolean | Meaning |
---|---|
TRUE | true value |
FALSE | false value |
BOOL | set of boolean values {TRUE, FALSE} |
bool(P) | convert predicate into BOOL value |
Sets:
Set expression or predicate | Meaining |
---|---|
{} | Empty Set |
{E} | Singleton Set |
{E,F,...} | Set Enumeration |
{x1,...,xn|P} | Set Comprehension |
POW(S) | Power Set |
POW1(S) | Set of Non-Empty Subsets |
FIN(S) | Set of All Finite Subsets |
FIN1(S) | Set of All Non-Empty Finite Subsets |
card(S) | Cardinality |
S * T | Cartesian Product |
S / T | Set Union |
S /\ T | Set Intersection |
S - T | Set Difference |
E : S | Element of |
E /: S | Not Element of |
S <: T | Subset of |
S /<: T | Not Subset of |
S <<: T | Strict Subset of |
S /<<: T | Not Strict Subset of |
union(S) | Generalized Union over Sets of Sets |
inter(S) | Generalized Intersection over Sets of Sets |
UNION(z1,...,zn).(P|E) | Generalized Union with Predicate |
INTER(z1,...,zn).(P|E) | Generalized Intersection with Predicate |
Restriction: Set comprehesions, generalized unions and generalized intersections are quantified constructs. The predicate P must be a conjunction where the first n conjuncts must constraint the bounded variables. The i-th conjunct must constraint xi for each i in {1,...,n}.
Numbers:
Number expression or predicate | Meaning |
---|---|
INTEGER | Set of Integers |
NATURAL | Set of Natural Numbers |
NATURAL1 | Set of Non-Zero Natural Numbers |
INT | Set of Implementable Integers |
NAT | Set of Implementable Natural Numbers |
NAT1 | Set of Non-Zero Implementable Natural Numbers |
n..m | Set of Numbers from n to m |
MININT | Minimum Implementable Integer |
MAXINT | Maximum Implementable Integer |
m > n | Greater Than |
m < n | Less Than |
m >= n | Greater Than or Equal |
m <= n | Less Than Or Equal |
max(S) | Maximum of a Set of Numbers |
min(S) | Minimum of a Set of Numbers |
m + n | Addition |
m - n | Difference |
m * n | Multiplication |
m / n | Division |
m ** n | Power |
m mod n | Remainder of Division |
PI(z1,...,zn).(P|E) | Set product |
SIGMA(z1,...,zn).(P|E) | Set summation |
succ(n) | Successor |
pred(n) | Predecessor |
Restrictions:
INTEGER, NATURAL and NATURAL1 are infinite sets. They are only supported on the right-hand side of a set predicate.
Set product and set summation are quantified constructs. The predicate P must be a conjunction where the first n conjuncts must constraint the bounded variables. The i-th conjunct must constraint xi for each i in {1,...,n}.
Relations:
Relation expression | Meaining |
---|---|
S <-> T | Set of relation |
E |-> F | Couple |
dom(r) | Domain of Relation |
range(r) | Range of Relation |
id(S) | Identity Relation |
S <| r | Domain Restriction |
S <<| r | Domain Substraction |
r |> S | Range Restriction |
r |>> S | Range Substraction |
r~ | Inverse of Relation |
r[S] | Relational Image |
r1 <+ r2 | Relational Overriding |
r1 >< r2 | Direct Product |
(r1 ; r2) | Relational Composition |
(r1 || r2) | Parallel Product |
prj1(S,T) | Projection Function |
prj2(S,T) | Projection Function |
closure1(r) | Transitive Closure |
closure(r) | Transitive Reflxibe Closure |
iterate(r,n) | Iteration of r with n |
fnc(r) | Translate Relation A <-> B into function A +-> POW(B) |
rel(r) | Translate Relation A <-> POW(B) into relation A <-> B |
Restriction: Set of Relation mostly grows up very fast. They are only supported on the right-hand side of a set predicate.
Functions:
Function Expression | Meaning |
---|---|
S +-> T | Partial Function |
S --> T | Total Function |
S +->> T | Partial Surjection |
S -->> T | Total Surjection |
S >+> T | Partial Injection |
S >+>> T | Partial Bijection |
S >->> T | Total Bijection |
%(x1,...,xn).(P|E) | Lambda Abstraction |
f(E) | Function Application |
f(E1,...,EN) | Function Application with Couples |
Restriction: Lambda expressions are quantified constructs. The predicate P must be a conjunction where the first n conjuncts must constraint the bounded variables. The i-th conjunct must constraint xi for each i in {1,...,n}.
Sequences:
Sequence Expression | Meaning |
---|---|
<> or [] | Empty Sequence |
[E] | Singleton Sequence |
[E1,...,EN] | Sequence with N elements |
size(S) | Size of Sequence |
s^t | Concatenation |
E -> s | Prepend element |
s <- E | Append element |
rev(S) | Reverse of Sequence |
first(S) | First Element |
last(S) | Last Element |
front(S) | Front of Sequence |
tail(S) | Tail of Sequence |
conc(S) | Concatenation of Sequence of Sequences |
s /|\ n | Take first n elements of sequence |
s |/ n | Drop first n elements of sequence |
The following constructs are not supported for code generation: seq(S), seq1(S), iseq(S), iseq1(S) and perm(S). They are only allowed in the predicate of constructs for verification such as invariant or precondition.
Records:
Record/Struct expression | Meaning |
---|---|
struct(ID1:T1,...,IDN:TN) | Set of Records with Given Fields and Field Types |
rec(ID1:E1,...,IDN:EN) | Record with Given Field Names and Values |
E'ID | Get value of field with name ID |
Nested record accesses are also supported.
Strings:
String Expression | Meaning |
---|---|
"string" | String Value |
STRING | Set of All Strings |
Restriction: STRING is a infinite set. It is only supported on the right-hand side of a set predicate.
LET and IF-THEN-ELSE Expression and Predicate:
Expression or Predicate | Notes |
---|---|
IF P THEN E1 ELSE E2 END | E1 and E2 are expressions or predicates |
LET x1,...,xn BE x1 = E1 & ... & xn = En IN E END | E is a predicate or a expression |
Substitution:
Substitution | Meaning |
---|---|
skip | No Operation |
x := E | Assignment |
f(X) := E | Functional Override |
f'ID := E | Record Access |
x :: S | Choice from Set |
x : (P) | Choice by Predicate |
x <-- OP(X) | Operation Call and Assignment of Return Value |
G || H | Parallel Substitution |
G ; H | Sequential Substitution |
ANY x1,...,xn WHERE P THEN G END | Non Deterministic Choice |
LET x1,...,xn BE x1=E1 & ... & xn = En IN G END | Let Substitution |
VAR x1,...,xn IN G END | Generate local variables |
PRE P THEN G END | Substitution with Precondition |
ASSERT P THEN G END | Substitution with Assertion |
CHOICE G or H END | Choice Substitution |
IF P THEN G END | IF Substitution |
IF P THEN G ELSE H | IF-THEN-ELSE Substitution |
IF P1 THEN G1 ELSIF P2 THEN G2 ... ELSE Gn END | IF-THEN-ELSE Substitution with Many Else Branches |
SELECT P THEN G WHEN .. WHEN Q THEN H END | SELECT Substitution with Many Branches |
SELECT P THEN G WHEN .. WHEN Q THEN H ELSE I END | SELECT Substitution with Many Branches and a Else Branch |
CASE E OF EITHER m THEN G or n THEN H ... END END | CASE substitution |
Functional Override and Record Access with assignment can be nested.
Preconditions and Assertions are constructs that are relevant for verification. They are ignored at code generation.
Assignments, Operation Calls, Choice from Set and Choice By Predicate can contain many variables on the left-hand side. Furthermore Choice By Predicate can use previous values of variables.
Restriction: Choice by Predicates are quantified constructs. The predicate P must be a conjunction where the first n conjuncts must constraint the bounded variables. The i-th conjunct must constraint xi for each i in {1,...,n}.
Comments are ignored during code generation. Furthermore trees and pragmas are not supported by B2Program.
Usage
Starting the code generator
Gradle
# Java
./gradlew run -Planguage="java" -Pbig_integer="true/false" [-Pminint="minint" -Pmaxint="maxint"] -Pdeferred_set_size="size" -Pfile="<path_relative_to_project_directory>"
# C++
./gradlew run -Planguage="cpp" -Pbig_integer="true/false" [-Pminint="minint" -Pmaxint="maxint"] -Pdeferred_set_size="size" -Pfile="<path_relative_to_project_directory>"
# Python
./gradlew run -Planguage="python" -Pbig_integer="true/false" [-Pminint="minint" -Pmaxint="maxint"] -Pdeferred_set_size="size" -Pfile="<path_relative_to_project_directory>"
# C
./gradlew run -Planguage="c" -Pbig_integer="true/false" [-Pminint="minint" -Pmaxint="maxint"] -Pdeferred_set_size="size" -Pfile="<path_relative_to_project_directory>"
-Pminint and -Pmaxint are optional. The default values cover 32-Bit Integers
JAR-File
- Run
./gradlew fatJar
to build the JAR-file - Move the built JAR-file
B2Program-all-0.1.0-SNAPSHOT
to the same folder as the machine - Generate code from the machine
# Java
java -jar B2Program-all-0.1.0-SNAPSHOT java <isBigInteger> <minint> <maxint> <deferred_set_size> <file_path_relative_to_jar_file>
# C++
java -jar B2Program-all-0.1.0-SNAPSHOT cpp <isBigInteger> <minint> <maxint> <deferred_set_size> <file_path_relative_to_jar_file>
# Python
java -jar B2Program-all-0.1.0-SNAPSHOT python <isBigInteger> <minint> <maxint> <deferred_set_size> <file_path_relative_to_jar_file>
# C
java -jar B2Program-all-0.1.0-SNAPSHOT c <isBigInteger> <minint> <maxint> <deferred_set_size> <file_path_relative_to_jar_file>
Note that minint and maxint are not optional when using JAR-File.
Compile the generated code in Java
- Build JAR for Java B Types (
make btypes_primitives
ormake btypes_big_integer
) - Move
btypes_persistent.jar
to same folder as the generated classes javac -cp btypes_primitives-all.jar <files....>
- Example:
javac -cp btypes_primitives-all.jar TrafficLightExec.java TrafficLight.java
(Code generated from TrafficLightExec.mch which includes TrafficLight.mch)
Compile the generated code in C++
- Install C++ B Types or move them (see btypes_primitives or btypes_big_integer folder) to same folder as the generated classes
g++ -std=c++14 -O2 -march=native -g -DIMMER_NO_THREAD_SAFETY -o <executable> <main class>
- Example:
g++ -std=c++14 -O2 -flto -march=native -g -DIMMER_NO_THREAD_SAFETY -o TrafficLightExec.exec TrafficLightExec.cpp
(TrafficLightExec.mch includes TrafficLight.mch, this command automatically compiles TrafficLight.cpp)
Compile the generated code in C
- Move BInteger and BBoolean to same folder as generated code (see btypes/src/main/c)
gcc <input file> -o <output file>
- Example:
gcc Lift.c -o Lift
Execute the generated code in Java
- Write a main function in the generated main class
java -cp :btypes_primitives-all.jar <main file>
- Example:
java -cp :btypes_primitives-all.jar TrafficLightExec
Execute the generated code in C++
- Write a main function in the generated main class
./<main file>
- Example:
./TrafficLightExec.exec
Execute the generated code in C
- Write a main function in the generated main file
./main.c
- Example:
./Lift
Steps from B Model to Output of the Generated Code (with primitive types)
Example 1: Lift
The file for Lift.mch
is in https://github.com/favu100/b2program/tree/master/src/test/resources/de/hhu/stups/codegenerator.
Lift consists of operation to lift up and lift down and getting the floor.
Java
- Run
./gradlew fatJar
to build the JAR-file - Move the built JAR-file
B2Program-all-0.1.0-SNAPSHOT
to the same folder asLift.mch
- Generate code for
Lift.mch
withjava -jar B2Program-all-0.1.0-SNAPSHOT.jar java false -2147483648 2147483647 10 Lift.mch
- Write a main method in
Lift.java
public static void main(String[] args) {
Lift lift = new Lift();
lift.inc();
lift.inc();
lift.inc();
System.out.println(lift.getFloor());
}
- Build B types in btypes_primitives
./gradlew fatJar
in the belonging folder or executemake
which builds btypes_primitives and move it to this folder - Move
btypes_primitives-all.jar
to the same folder asLift.java
- Compile
Lift.java
withjavac -cp btypes_primitives-all.jar Lift.java
- Execute the compiled file for
Lift.java
withjava -cp :btypes_primitives-all.jar Lift
- Output:
3
C++
- Run
./gradlew fatJar
to build the JAR-file - Move the built JAR-file
B2Program-all-0.1.0-SNAPSHOT
to the same folder asLift.mch
- Generate code for
Lift.mch
java -jar B2Program-all-0.1.0-SNAPSHOT.jar cpp false -2147483648 2147483647 10 Lift.mch
- Write a main method in
Lift.cpp
int main() {
Lift lift;
lift.inc();
lift.inc();
lift.inc();
cout << lift.getFloor() << "\n";;
return 0;
}
- Install C++ B types with
mkdir build & cd build
cmake ..
make install
- Compile
Lift.cpp
withg++ -std=c++14 -O2 -flto -march=native -g -DIMMER_NO_THREAD_SAFETY -o Lift.exec Lift.cpp
- Execute
Lift.exec
with./Lift
Example 2: Cruise_finite1_deterministic_exec
The file for Cruise_finite1_deterministic_exec.mch
and Cruise_finite1_deterministic
are in https://github.com/favu100/b2program/tree/master/src/test/resources/de/hhu/stups/codegenerator.
The machine Cruise_finite1_deterministic_exec includes the machine Cruise_finite1_deterministic.
Cruise_finite1_deterministic_exec contains an operation for executing a cycle in the state space of Cruise_finite1_deterministic 100.000 times.
Furthermore it has getter operations for all variables.
Java
- Run
./gradlew fatJar
to build the JAR-file - Move the built JAR-file
B2Program-all-0.1.0-SNAPSHOT
to the same folder asCruise_finite1_deterministic_exec.mch
andCruise_finite1_deterministic.mch
- Generate code for
Cruise_finite1_deterministic_exec.mch
java -jar B2Program-all-0.1.0-SNAPSHOT.jar java false -2147483648 2147483647 10 Cruise_finite1_deterministic.mch
- Write a main method in
Cruise_finite1_deterministic_exec.java
public static void main(String[] args) {
Cruise_finite1_deterministic_exec cruise = new Cruise_finite1_deterministic_exec();
cruise.simulate();
System.out.println(cruise.getCruiseAllowed());
System.out.println(cruise.getCruiseActive());
System.out.println(cruise.getVehicleAtCruiseSpeed());
System.out.println(cruise.getVehicleCanKeepSpeed());
System.out.println(cruise.getVehicleTryKeepSpeed());
System.out.println(cruise.getSpeedAboveMax());
System.out.println(cruise.getVehicleTryKeepTimeGap());
System.out.println(cruise.getCruiseSpeedAtMax());
System.out.println(cruise.getObstaclePresent());
System.out.println(cruise.getObstacleDistance());
System.out.println(cruise.getObstacleRelativeSpeed());
System.out.println(cruise.getObstacleStatusJustChanged());
System.out.println(cruise.getCCInitialisationInProgress());
System.out.println(cruise.getCruiseSpeedChangeInProgress());
}
- Build B types in btypes_primitives with
./gradlew fatJar
in the belonging folder or executemake
which builds btypes_primtives and move it to this folder - Move
btypes_primitives-all.jar
to the same folder as the generated classes - Compile
Cruise_finite1_deterministic_exec.java
andCruise_finite1_deterministic.java
withjavac -cp btypes_primitives-all.jar Cruise_finite1_deterministic_exec.java Cruise_finite1_deterministic.java
- Execute the compiled file for
Cruise_finite1_deterministic_exec.java
withjava -cp :btypes_primitives-all.jar Cruise_finite1_deterministic_exec
- Output:
false
false
false
false
false
false
false
false
false
ODnone
RSnone`
false
false
false
C++
- Run
./gradlew fatJar
to build the JAR-file - Move the built JAR-file
B2Program-all-0.1.0-SNAPSHOT
to the same folder asCruise_finite1_deterministic_exec.mch
andCruise_finite1_deterministic.mch
- Generate code for
Cruise_finite1_deterministic_exec.mch
java -jar B2Program-all-0.1.0-SNAPSHOT.jar cpp false -2147483648 2147483647 10 Cruise_finite1_deterministic_exec.mch
- Write a main method in
Cruise_finite1_deterministic_exec.cpp
int main() {
Cruise_finite1_deterministic_exec cruise;
cruise.simulate();
cout << cruise.getCruiseAllowed() << "\n";
cout << cruise.getCruiseActive() << "\n";
cout << cruise.getVehicleAtCruiseSpeed() << "\n";
cout << cruise.getVehicleCanKeepSpeed() << "\n";
cout << cruise.getVehicleTryKeepSpeed() << "\n";
cout << cruise.getSpeedAboveMax() << "\n";
cout << cruise.getVehicleTryKeepTimeGap() << "\n";
cout << cruise.getCruiseSpeedAtMax() << "\n";
cout << cruise.getObstaclePresent() << "\n";
cout << cruise.getObstacleDistance() << "\n";
cout << cruise.getObstacleRelativeSpeed() << "\n";
cout << cruise.getObstacleStatusJustChanged() << "\n";
cout << cruise.getCCInitialisationInProgress() << "\n";
cout << cruise.getCruiseSpeedChangeInProgress() << "\n";
return 0;
}
- Install C++ B types with
mkdir build & cd build
cmake ..
make install
- Compile
Cruise_finite1_deterministic_exec.cpp
withg++ -std=c++14 -O2 -flto -march=native -g -DIMMER_NO_THREAD_SAFETY -o Cruise_finite1_deterministic_exec.exec Cruise_finite1_deterministic_exec.cpp
- Execute
Cruise_finite1_deterministic_exec.exec
with./Cruise_finite1_deterministic_exec
- Output:
false
false
false
false
false
false
false
false
false
ODnone
RSnone`
false
false
false
Performance
Analysis of the Performance is described in benchmarks/README.md.