VDM Values in Java - overturetool/vdm-vscode GitHub Wiki

Using VDM Values in Java

Integration between VS Code and Java code can be established, either by writing native libraries in Java that can be called from VDM or by giving a Java program overall control of a VDM model by making calls to that model as a user interacts with a GUI. In both cases, internal VDM values have to be handled by Java -– either because they are passed as arguments to a Java library, and returned as results to VDM, or because they are returned from a VDM model evaluation to a controlling Java program. This page describes the internal class hierarchy used by VDMJ to represent internal VDM model values and describes how a Java program can convert these to Java values (int, long, String etc.) as well as creating internal values for returning to the VDM model (e.g. as the return value of library methods).

The Value Class Hierarchy

All internal VDM values in VDMJ are held by instances of the Value class with the fully qualified name, com.fujitsu.vdmj.values.Value. The Value class itself is abstract, but subclasses can be instantiated to represent any VDM value, such as a “seq of char”, “nat1’” or a value of an arbitrarily complex type. The hierarchy is shown in the figure below.

Generally, the name of the Value subclass for a VDM type is on the form <name>Value, for example BooleanValue or SeqValue.

The following sections describe how to obtain Java values from a Value object, and how to create Value objects from basic Java values (or iteratively from other values).

Primitive Values

Most primitive VDM types have subclasses with simple constructors that take a Java primitive type as an argument:

public BooleanValue(boolean value)
public CharacterValue(char value)
public RealValue(double value) throws Exception
public RationalValue(double value) throws Exception
public IntegerValue(long value)
public NaturalValue(long value) throws Exception
public NaturalOneValue(long value) throws Exception
public QuoteValue(String value)
public NilValue()

The constructors that throw exceptions are the ones for which some Java value does not match the VDM type concerned. For example, a RealValue or RationalValue cannot take the Java values Double.NaN or Double.POSITIVE_INFINITY as a constructor argument. Similarly, NaturalValue and NaturalOneValue cannot take a negative long as an argument.

Note that a QuoteValue is constructed with a string. This is simply the string value that would appear between angle brackets in VDM, for example <FAIL> would be constructed with the Java string "FAIL".

To convert a VDM value into a Java value, the Value class provides a number of conversion methods, each of which returns the corresponding Java primitive value, or throws an exception if the conversion cannot be made for the VDM type concerned:

public boolean boolValue(Context ctxt) throws ValueException
public char charValue(Context ctxt) throws ValueException
public double realValue(Context ctxt) throws ValueException
public double ratValue(Context ctxt) throws ValueException
public long intValue(Context ctxt) throws ValueException
public long natValue(Context ctxt) throws ValueException
public long nat1Value(Context ctxt) throws ValueException
public String quoteValue(Context ctxt) throws ValueException

Note that all of these conversion functions take a Context parameter as argument and potentially throw a ValueException. The Context parameter is used internally by VDMJ and represents the call stack during the evaluation of an expression. This parameter can be set to null when using these methods in Java code outside VDMJ. A ValueException is thrown if the VDM value cannot be converted into the Java type requested. For example, calling booleanValue on a RealValue object will raise a ValueException with the message text "Can’t get bool value of real".

Sets, Sequences and Maps

VDM allows primitive types to be built into more complex aggregations and collections, and these can also be converted to and from Java types, though the process is a little more involved. Three classes are provided to assist with this conversion: ValueSet, ValueList and ValueMap (all within the same com.fujitsu.vdmj.values package). These classes represent, respectively, a Java Set, List and Map of VDM values:

public class ValueSet extends Vector<Value>
public class ValueList extends Vector<Value>
public class ValueMap extends LinkedHashMap<Value, Value>

Note that the ValueSet class is actually based on a Java Vector, not a Java Set type, though the class does have set semantics (no duplicates). This is an implementation detail and allows VDMJ to permute set orderings in certain circumstances.

These three classes have obvious constructors, and allow Values (or collections of them) to be added to the collection subsequently, using standard Java collection methods:

public ValueSet()
public ValueSet(int size)
public ValueSet(ValueSet from)
public ValueSet(Value v)

public ValueList()
public ValueList(ValueList from)
public ValueList(Value v)
public ValueList(int size)

public ValueMap()
public ValueMap(ValueMap from)
public ValueMap(Value k, Value v)

Using these three helper classes, it is now possible to create VDM set, sequence and map values, using constructors of the SetValue, SeqValue and MapValue classes:

public SetValue()
public SetValue(ValueSet values)

public SeqValue()
public SeqValue(ValueList values)
public SeqValue(String s)

public MapValue()
public MapValue(ValueMap values)

Note that there is a special constructor for SeqValue that takes a Java string. This creates a VDM seq of char, but without the need to create a ValueList with CharacterValues.

If the ValueList (or another) collection passed to these constructors contains a mixture of VDM types -– i.e. a mixture of VDM Value subclasses, such as a BooleanValue and a NaturalOneValue -– then the type of the constructed VDM value is the union of the various types passed, in this example seq of (bool j nat1). If this VDM type is not compatible with the use of a corresponding value in the VDM model a dynamic type exception occurs when the value is processed by the model.

Lastly, as before, to get the primitive Java values of a VDM collection, the following methods are provided:

public ValueList seqValue(Context ctxt) throws ValueException
public String stringValue(Context ctxt) throws ValueException
public ValueSet setValue(Context ctxt) throws ValueException
public ValueMap mapValue(Context ctxt) throws ValueException

Note that, as with the SeqValue constructor, there is a special method to return a Java String from a seq of char SeqValue, rather than a ValueList of CharacterValues. As before, if the Value being used is not a sequence, set or map, then these methods will throw a ValueException.

Other Types

The sections above describe how to create or deconstruct simple VDM values in Java as well as simple collections of these. The remainder of this section describes the unusual cases, for more sophisticated types.

Function Values

VDMJ has an internal FunctionValue class used for holding values of functions (e.g. the value of a "lambda" expression or the value of a function defined within a module). But as far as Java is concerned, these values are opaque -– there is no equivalent Java construct, and the only way to evaluate a VDM function is to let VDMJ perform that evaluation. Similarly, Java cannot construct a FunctionValue.

The only operation that Java can reasonably perform with a FunctionValue is to create a composite function (eg. f1 comp f2 in VDM) or a function iteration (e.g. f ** 3 in VDM) using existing FunctionValues. In order to do this, there are two subclasses of FunctionValue, called CompFunctionValue and IterFunctionValue, the constructors for which are as follows:

public CompFunctionValue(FunctionValue f1, FunctionValue f2)
public IterFunctionValue(FunctionValue function, long count)

These both create new FunctionValues, which when evaluated by VDMJ act as the composition and iteration of the arguments, respectively.

There is a method for obtaining a FunctionValue from a Value, but note that this is not an internal Java value (unlike other Value methods, like realValue). It is used as a more convenient way of casting the Value to a FunctionValue.

public FunctionValue functionValue(Context ctxt)

Object Values

When VDM++ and VDM-RT create new objects using the new operator, the resulting values are held as ObjectValues in VDMJ. These are complex types that involve function and operation definitions for the object as well as any type, value, sync, thread or traces sections defined. Therefore ObjectValues are really opaque to Java and cannot be used directly.

Like for FunctionValue, the ObjectValue class has a method for converting a Value into an ObjectValue:

public ObjectValue objectValue(Context ctxt)

Record Values

A VDM record is just a collection of typed field values. A RecordValue can be obtained from a Value using the following method, which returns a RecordValue rather than some other Java representation:

public RecordValue recordValue(Context ctxt)

To get individual field values from a RecordValue, two more Java helper types have to be introduced, called FieldMap and FieldValue. A FieldValue has the following constructor, and represents a record field:

public FieldValue(String name, Value value, boolean comparable)

The comparable argument indicates whether this field is used in the value comparison between record values. A field declared with “-” in VDM would have a false argument, but normally this argument would be true, and the value must match the record type being used. FieldValues are added to a FieldMap, which is just a Java List of FieldValues.

So given a RecordValue, its FieldMap can be obtained from a public final field in the object, called fieldMap, and from there, individual FieldValues can be accessed -– e.g. fieldMap.get(0).name and fieldmap.get(0).value.

To create a RecordValue, the record type is obtained from the RemoteInterpreter:

type = remoteInterpreter.getInterpreter().findType(typename)

The type is then passed to the RecordValue constructor, along with a FieldMap or a ValueList (of the fields in order).

public RecordValue(RecordType type,
                   ValueList values,
                   Context ctxt)
public RecordValue(RecordType type,
                   FieldMap mapvalues,
                   Context ctxt)

The Context parameter is needed to allow records with invariants to check the invariant before the value is constructed. Note that currently, record types with an invariant cannot be constructed in Java. The Context parameter can be passed as null from Java.

The caller is responsible for passing field values that match their expected type. If they do not match, VDMJ throws a dynamic type exception for subsequent evaluations.

Token Values

Token values are simply wrappers for normal VDM values, reflecting the way they are created in VDM, like mk_token("hello"), which would be a wrapper for a seq of char. There is no special way of getting a TokenValue from a Value, other than casting it. Having casted the Value, the wrapped value can be obtained from the public final Value field called "value".

Constructing a TokenValue is just a matter of passing the Value required:

public TokenValue(Value exp)

Tuple Values

A TupleValue in VDMJ is a wrapper for a ValueList. The following method and constructor can be used like one would expect:

public TupleValue(ValueList argvals)
public ValueList tupleValue(Context ctxt)

Invariant Values

A VDM type can be given a name and an invariant, e.g. when wrapping a primitive type without an invariant. VDMJ has a separate Value subclass for values of such types that simply combine the primitive Value with a FunctionValue for the invariant. However, as with RecordValues (which can also have invariants), it is not currently possible to create InvariantValues in Java for types that have an invariant. For types without an invariant, the constructor is as follows:

public InvariantValue(NamedType type, Value value, Context ctxt)

The NamedType is obtained in a similar way to the RecordType above, using the RemoteInterpreter. Note that the caller is responsible for passing a Value that matches the expected type. If they do not match, VDMJ will throw a dynamic type exception for subsequent evaluations.

Void Values

Operations which do not return a value in VDM (i.e. ==> ()) return an instance of VoidValue in Java. The constructor has no arguments.

⚠️ **GitHub.com Fallback** ⚠️