type ::= "Any" | "Boolean" | ID_basic | featureType | "Option" "[" type "]" | "Either" "[" type "," type "]" | "(" type ( "," type )+ ")" | "Seq" "[" type "]" | "Set" "[" type "]" initialization ::= "true" | "false" | <scalaExp : basic type value creation> | "new" featureType [ "{" attributeInit "}" ] | "None" | "Some" "(" initialization ")" | "Left" "(" initialization ")" | "Right" "(" initialization ")" | "(" initialization ( "," initialization )+ ")" | "Seq" "(" [ initialization ( "," initialization )* ] ")" | "Set" "(" [ initialization ( "," initialization )* ] ")" | "DYN" attributeInit ::= attributeAnnotation* "val" ID_attribute ":" type "=" initialization
A type can refer to scala.Any, dms.Boolean, a declared basic type, or a declared feature (or a compound of declared features). In addition, DML (DMS) supports optional type (scala.Option), either (choice) type (scala.util.Either), tuple type (scala.Tuple2, scala.Tuple3, ..., scala.Tuple22), sequence type (scala.collection.immutable.Seq), and set type (scala.collection.immutable.Set). The scala.util.Either type only supports a choice between two types. In the future, DMS will provide choice types for more than two types; however, for type safety guarantee, similar tricks to Scala tuple types need to be provided (e.g., Either3, Either4, ...) and handled by the dms.ModelExtractor.
The scala.Any type can only be used as a placeholder for further attribute type refinement and cannot be directly initialized; that is, the attribute type should be refined before initializing it.
Attribute whose type is dms.Boolean can be initialized directly by using Scala’s true or false literal; the literal will be implicitly converted to dms.Boolean. For example:
1 2 3 4 5 | import edu.ksu.cis.santos.mdcf.dms._
trait ExsBooleanInit extends Feature {
val foo : Boolean = true
}
|
Note that an implicit conversion is also provided from dms.Boolean to scala.Boolean for convenience; for example, it is (implicitly) used in dms.example.requirement.MyReqPulseOx at line 22 and line 33 because scala.collection.immutable.Set exists accepts a function that returns a scala.Boolean value.
Attribute whose type is a basic type can be initialized using a Scala expression (<scalaExp: basic type value creation>) as described in the Basic Type Section such as by using its implicit factory method (if defined). For example:
1 2 3 4 5 6 | import edu.ksu.cis.santos.mdcf.dms._
import edu.ksu.cis.santos.mdcf.dms.example._
trait ExsBasicType extends Feature {
val foo : Int = 5
}
|
Attribute whose type is a feature (or a compound of features) can be initialized by creating (new) the feature with attribute initializations. For example, see NoninPulseOx.manufacturerModel.
Initializations for the other types use the typical Scala factory methods associated with the types, whose usage syntax are specified above. One thing worth mentioning is that, by default, Seq(...) creates a scala.collection.Seq instead of scala.collection.immutable.Seq that is used in DMS; DMS provides an implicit conversion from scala.collection.Seq to scala.collection.immutable.Seq for convenience. On the other hand Set(...) creates scala.collection.immutable.Set, hence, no conversion is necessary. The DYN initialization indicates an attributes whose value is dynamic and not part of the device model.
Below is a table that maps type and initialization grammar rules to DML AST classes that represent them.
Grammar Rules | DML AST Classes |
---|---|
Any | dml.ast.NamedType with name = "Any" |
Boolean | dml.ast.NamedType with name = "Boolean" |
ID_basic | dml.ast.NamedType with name = fully-qualified name of the basic type |
ID_feature | dml.ast.NamedType with name = fully-qualified name of the feature |
ID_feature ( "with" ID_feature )+ | dml.ast.RefinedType (with empty attributes) |
"Option" "[" type "]" | dml.ast.OptionType |
"Either" "[" type ”,” type "]" | dml.ast.EitherType |
"(" type ( "," type )+ ")" | dml.ast.TupleType |
"Set" "[" type "]" | dml.ast.SetType |
"Seq" "[" type "]" | dml.ast.SeqType |
"true" | dml.ast.BasicInit with value = "true" |
"false" | dml.ast.BasicInit with value = "false" |
<scalaExp : basic type value creation> | dml.ast.BasicInit with value = the result of dms.BasicType asString |
"new" featureType [ "{" attributeInit "}" ] | dml.ast.FeatureInit |
attributeInit | dml.ast.Attribute |
"None" | dml.ast.NoneInit |
"Some" "(" initialization ")" | dml.ast.SomeInit |
"Left" "(" initialization ")" | dml.ast.EitherInit with index = 0 |
"Right" "(" initialization ")" | dml.ast.EitherInit with index = 1 |
"Seq" "(" [ initialization ( "," initialization )* ] ")" | dml.ast.SetInit |
"Set" "(" [ initialization ( "," initialization )* ] ")" | dml.ast.SeqInit |
As can be observed dml.ast.EitherType and dml.ast.EitherInit are designed to support more than two choice types. Moreover, dml.ast.RefinedType supports declaring attributes or attribute refinements; however, we currently reserve this for future needs (hence, we do not describe it here at this point).
In general, type refinement is determined based on sub-typing (<:). More specifically:
For all type T, Any (scala.Any) can be refined by T.
A basic type T1 can be refined by another basic type T2 if,
T2 <: T1.
A feature F1 can be refined by another feature F2 if,
F2 <: F1.
A compound feature type CF = F1 with ... with FN can be refined by a feature F if,
for all i where 1 <= i <= N, F <: Fi.
For a set of features FS = { F1, ..., FN }, let With(FS) = F1 with ... with FN. A compound feature type CF1 = F11 with ... with F1N can be refined by another compound feature type CF2 = F21 with ... with F2M if,
for all i where 1 <= i <= N, there exists subCF2 in PowerSet({ F21, ..., F2M }) such that With(subCF2) refines F1i.
To put it simply, a compound feature type CF1 can be refined by another compound feature type CF2 if,
{ F1 | F1i <: F1, 1 <= i <= N } is a subset of { F2 | F2j <: F2, 1 <= j <= M }.
An option type Option[ T1 ] can be refined by Option[ T2 ] if,
T1 can be refined by T2.
An either type Either[ T1 , T2 ] can be refined by Either[ T3 , T4 ] if,
T1 can be refined by T3 and T2 can be refined by T4.
A tuple type ( T11 , ... , T1N ) can be refined by another tuple type ( T21 , ... , T2N ) if,
for all i where 1 <= i <= N, T1i can be refined by T2i.
A sequence type Seq[ T1 ] can be refined by Seq[ T2 ] if,
T1 can be refined by T2.
A set type Set[ T1 ] can be refined by Set[ T2 ] if,
T1 can be refined by T2.
Note
The <: relation is reflexive. That is, for all type T, T <: T.