Module ddsl.xtypes
Datatypes in Lua.
Uses the ddsl core primitives to implement datatypes (a.k.a. xtypes) as defined by the OMG X-Types specification in Lua.
The datatypes are equivalent to those described by the X-Types specification. Thus, this module can serve as an alternative for defining types in Lua, instead of in IDL or XML.
A datatype is a blueprint for a data structure. Any
number of data-objects or instances (a.k.a. xinstance
) can be created
from a datatype. Each instance is backed by the underlying datatype from
which it was created. The datatype constraints are enforced on the
instances---for example a struct instance can only have the fields defined
in the struct datatype; sequence and array bounds are enforced.
Furthermore, if the datatype structure changes, those changes are propagated
to all the instances.
Operators to manipulate datatypes (i.e. structure) are also provided. Thus, for a struct datatype, new members can be added, existing ones can be removed or modified. Since every instance is backed by a datatype, any instance can be used as a handle to manipulate the underlying datatype. Changes are propagated to all the other instances, to ensure consistency.
Since every instance is backed by a datatype, a new instance can be created
from any instance using new_instance. A datatype constructor returns
a template instance (a.k.a. xtemplate
) that can be used as cannonical
instance to refer to the datatype.
Each field in the template instance is initialized to a flattened out accessor string that can be used retrive that field's value in some storage system. For example, the accessor strings can be directly used for data access in Lua scripts used with the RTI Connext DDS Prototyper (see Section 8.4, Data Access API in the RTI Connext DDS Prototyper Getting Started Guide).
General Syntax
Since ddsl does not impose any specific syntax on how datatypes are expressed in Lua, this module defines the syntax. The general syntax and patterns common to all datatypes are illsurated below.
local xtypes = require 'ddsl.xtypes' -- create a datatype of kind 'kind' with the name 'MyType' -- NOTE:mytype
is the template instance forMyType
(i.e.xtemplate
) local mytype = xtypes.<kind>{ MyType = { <definition_syntax_for_kind> } } -- create an instance to use in user application code local myinstance = xtypes.new_instance(mytype) -- print the datatype underlying an instance print(tostring(myinstance)) -- or simply print(myinstance) -- get the template instance assert(xtypes.template(myinstance) == mytype) -- print the kind of a datatype -- NOTE: the kind is immutable, i.e. cannot be changed after creation print(mytype[xtypes.KIND]()) -- get the name of a datatype print(mytype[xtypes.NAME]) -- set the name of a datatype mytype[xtypes.NAME] = 'MyTypeNewName' -- get the enclosing namespace of a datatype print(mytype[xtypes.NS]) -- set the enclosing namespace of a datatype mytype[xtypes.NS] =YourType
-- defined elsewhere; maybenil
-- get the qualifiers associated with a datatype print(mytype[xtypes.QUALIFIERS]) -- set the qualifiers associated with a datatype mytype[xtypes.QUALIFIERS] = { xtypes.Nested, xtypes.Extensibility{'FINAL_EXTENSIBILITY'}, } -- Get the qualified name (i.e. scoped name) of a datatype: print(xtypes.nsname(mytype) -- Get the outermost enclosing scope of a datatype: print(xtypes.nsroot(mytype)
The datatype constructors provide more usage examples, specific to each type.
Info:
- Author: Rajive Joshi
Functions
nsname () | ddsl.nsname: fully qualified name within the enclosing scope. |
nsroot () | ddsl.nsroot: outermost enclosing root namespace. |
resolve () | ddsl.resolve: resolve a typedef |
template () | ddsl.template: get the cannonical template instance (xtemplate ) |
new_instance () | ddsl.new_instance: create a new instance (xinstance ) |
new_collection () | ddsl.new_collection: create a new collection of instances (xinstances ) |
is_collection () | ddsl.is_collection: is this a collection of instances (xinstances ) ? |
Tables
EMPTY | ddsl.EMPTY: Empty datatype definition for use as initializer in datatype constructors. |
Fields
log | logger to log messages and get/set the verbosity levels |
Datatype Kinds
ANNOTATION () | Annotation kind. |
ATOM () | atom kind. |
ENUM () | enumeration kind. |
STRUCT () | struct kind. |
UNION () | union kind. |
TYPEDEF () | typedef kind. |
CONST () | constant kind. |
MODULE () | module kind. |
Datatype Attributes
KIND () | ddsl.KIND: Datatype kind. |
NAME () | ddsl.NAME: Datatype name. |
NS () | ddsl.NS: Datatype enclosing namespace (enclosing scope). |
QUALIFIERS () | ddsl.QUALIFIERS: Datatype qualifiers (annotations). |
BASE () | Datatype of the base struct (inheritance) |
SWITCH () | Datatype of a union discriminator (switch). |
Datatype Qualifiers
annotation (decl) | Create an annotation. |
array (n, ...) | Create an array qualifier with the specified dimensions. |
sequence (n, ...) | Create a sequence qualifier with the specified dimensions. |
Datatypes
atom (decl) | Create an atomic datatype. |
enum (decl) | Create an enum datatype. |
struct (decl) | Create a struct datatype. |
union (decl) | Create a union datatype. |
typedef (decl) | Create a typedef alias datatype. |
const (decl) | Create a constant. |
module (decl) | Create a module namespace. |
Builtin Datatypes
boolean | boolean. |
octet | octet. |
char | char. |
wchar | wide char. |
float | float. |
double | double. |
long_double | long double. |
short | short. |
long | long. |
long_long | long long. |
unsigned_short | unsigned short. |
unsigned_long | unsigned long. |
unsigned_long_long | unsigned long long. |
string (n) | string<n> : string of length n. |
wstring (n) | wstring<n> :wstring of length n. |
Builtin Annotations
Key | Datatype member is a key field. |
ID | Datatype member id. |
Optional | Datatype member is optional. |
MustUnderstand | Datatype member is required. |
Shared | Datatype member is shared. |
BitBound | enum datatype is bit-bound. |
BitSet | enum datatype is a bit-set. |
Extensibility | Datatype extensibility. |
Nested | Datatype is not top-level it is nexted. |
top_level | Datatype may (or may not) be top-level. |
Functions
- nsname ()
- ddsl.nsname: fully qualified name within the enclosing scope.
- nsroot ()
-
ddsl.nsroot: outermost enclosing
root
namespace. - resolve ()
- ddsl.resolve: resolve a typedef
- template ()
-
ddsl.template: get the cannonical template instance (
xtemplate
) - new_instance ()
-
ddsl.new_instance: create a new instance (
xinstance
) - new_collection ()
-
ddsl.new_collection: create a new collection of instances (
xinstances
) - is_collection ()
-
ddsl.is_collection: is this a collection of instances (
xinstances
) ?
Tables
- EMPTY
-
ddsl.EMPTY: Empty datatype definition for use as initializer in
datatype constructors.
Usage:
local xtypes = require 'ddsl.xtypes' -- create an empty datatype of kind 'kind' with the name 'MyType' local mytype = xtypes.<kind>{ MyType = EMPTY }
Fields
- log
- logger to log messages and get/set the verbosity levels
Datatype Kinds
Usage:
-- get the kind of a datatype print(mytype[KIND]()) -- evaluate the kind to get the description -- use the kind of a datatype to make decisions if STRUCT == mytype[KIND] then ... else ... end
- ANNOTATION ()
-
Annotation kind.
Returns:
-
string
'annotation'
- ATOM ()
-
atom kind.
Returns:
-
string
'atom'
- ENUM ()
-
enumeration kind.
Returns:
-
string
'enum'
- STRUCT ()
-
struct kind.
Returns:
-
string
'struct'
- UNION ()
-
union kind.
Returns:
-
string
'union'
- TYPEDEF ()
-
typedef kind.
Returns:
-
string
'typedef'
- CONST ()
-
constant kind.
Returns:
-
string
'const'
- MODULE ()
-
module kind.
Returns:
-
string
'module'
Datatype Attributes
Usage:
-- get the name of a datatype print(mytype[NAME]) -- set the name of a datatype mytype[NAME] = 'MyTypeNewName'
- KIND ()
- ddsl.KIND: Datatype kind.
- NAME ()
- ddsl.NAME: Datatype name.
- NS ()
- ddsl.NS: Datatype enclosing namespace (enclosing scope).
- QUALIFIERS ()
- ddsl.QUALIFIERS: Datatype qualifiers (annotations).
- BASE ()
-
Datatype of the base struct (inheritance)
Returns:
-
string
' : '
- SWITCH ()
-
Datatype of a union discriminator (switch).
Returns:
-
string
'switch'
Datatype Qualifiers
- annotation (decl)
-
Create an annotation.
Annotations qualify a datatype or a member of the datatype. Except for
array and sequence qualifiers, annotation contents are opaque to
DDSL; they are kept intact and may be interpreted in the user's context.
Parameters:
- decl {[string]=...} a table containing an annotation name and definition, where ... are the optional default attributes of the annotation.
Returns:
-
table
an annotation datatype template (
xtemplate
)Usage:
-- Create user defined annotation @MyAnnotation(value1 = 42, value2 = 42.0) local MyAnnotation = xtypes.annotation{ MyAnnotation = {value1 = 42, value2 = 9.0} -- default attributes } -- Use user defined annotation with custom attributes MyAnnotation{value1 = 942, value2 = 999.0} -- Print the annotation contents (value1, value2) for k, v in pairs(MyAnnotation) do print(k, v) end -- Use builtin annotation Key xtypes.Key -- Use builtin annotation Extensibility xtypes.Extensibility{'EXTENSIBLE_EXTENSIBILITY'}
- array (n, ...)
-
Create an array qualifier with the specified dimensions.
Ensures that a valid set of dimension values is passed in. Returns the
array datatype qualifier, initialized with the specified dimensions.
An array qualifier is interpreted by DDSL as a collection qualifier.
Parameters:
- n int the first dimension
- ... the remaining dimensions
Returns:
-
table
the array qualifier instance (
xtemplate
) - sequence (n, ...)
-
Create a sequence qualifier with the specified dimensions.
Ensures that a valid set of dimension values is passed in. Returns the
sequence datatype qualifier, initialized with the specified dimensions.
A sequence qualifier is interpreted by DDSL as a collection qualifier.
Parameters:
- n int the first dimension
- ... the remaining dimensions
Returns:
-
table
the sequence qualifier instance (
xtemplate
)
Datatypes
- atom (decl)
-
Create an atomic datatype. There are two kinds of atomic types:
- un-dimensioned
- dimensioned, e.g. bounded size/length (e.g. string
)
Parameters:
- decl {[string]=EMPTY, {int} or const} a table containing an atom name mapped to and EMPTY initializer (for undimensioned atoms) or a a table containing an integral dimension (for dimensioned atoms). The dimension could also be an integral const datatype.
Returns:
-
table
an atom datatype template (
xtemplate
)Usage:
-- Create an un-dimensioned atomic datatype named 'MyAtom': local MyAtom = xtypes.atom{ MyAtom = EMPTY } -- Create a dimensioned atomic type: local string10 = xtypes.atom{string={10}} -- bounded length string local wstring10 = xtypes.atom{wstring={10}} -- bounded length wstring -- Create a dimensioned atomic type, where 'MAXLEN' is a const: local StringMaxlen = xtypes.atom{string=MAXLEN} -- bounded length string
- enum (decl)
-
Create an enum datatype.
Parameters:
- decl
{[string]={string,string,[string]=int,[string]=int,...}
a table containing an enum name mapped to a table (which is an array of strings or a map of strings to ordinal values, or a mix of both). For example,
{ MyEnum = { { str1 = ord1 }, { str2 = ord2 }, ... } }
or
{ MyEnum = { str1, str2, ... } }
or a mix of the above
{ MyEnum = { strA, strB, { str1 = ord1 }, { str2 = ord2 }, ... } }
Returns:
-
table
an enum datatype template (
xtemplate
) The table is a map of enumerator strings to their ordinal values.Usage:
-- Create enum: declarative style local MyEnum = xtypes.enum{ MyEnum = { { enumerator_1 = ordinal_1 }, : { enumerator_M = ordinal_M }, -- OR -- enumerator_A, : enumerator_Z, -- OPTIONAL -- annotation_x, : annotation_z, } } -- Create enum: imperative style (start with EMPTY) MyEnum = xtypes.enum{ MyEnum = xtypes.EMPTY } -- Set the i-th member: MyEnum[i] = { enumerator_i = ordinal_i } -- OR -- MyEnum[i] = enumerator_i -- default:
ordinal
_i = #MyEnum -- After setting the i-th member, the following post-conditions hold: MyEnum[enumerator_i] == ordinal_i MyEnum(ordinal_i) == enumerator_i -- Delete the i-th member: MyEnum[i] = nil -- Get the number of enumerators in the enum: print(#MyEnum) -- Get the i-th member: local enumerator, ordinal = next(MyEnum[i]) print(enumerator, ordinal) -- Get the enumerator's ordinal value: print(MyEnum[enumerator]) -- Iterate over the model definition (ordered): for i = 1, #MyEnum do print(next(MyEnum[i])) end -- Iterate over enum and ordinal values (unordered): for enumerator, ordinal in pairs(MyEnum) do print(enumerator, ordinal) end -- Lookup the enumerator name for an ordinal value: print(MyEnum(ordinal)) - decl
{[string]={string,string,[string]=int,[string]=int,...}
- struct (decl)
-
Create a struct datatype.
Parameters:
- decl
{[string]={[string]={...},{[string]={...},...}
a table containing a struct name mapped to a table (which is an array of strings mapped to member definitions). For example,
{ MyStruct = { { role1 = {...}, { role2 = {...} }, ... } }
where the member definition for a role is,
{ role = { xtemplate, [array | sequence,] [annotation, ...] } }
Returns:
-
table
a struct datatype template (
xtemplate
).
The table is a map of the role names to flattened out strings that represent the path from the enclosing top-level struct scope to the role. The string values may be be used to retrieve the field values from some storage system.Usage:
-- Create struct: declarative style local MyStruct = xtypes.struct{ MyStruct = { [<optional base struct>], -- base struct must be the 1st item {role_1={xtemplate_1, [array_1|sequence_1,] [annotation_1,...]}}, : {role_M={xtemplate_M, [array_M|sequence_M,] [annotation_M,...]}}, -- OPTIONAL -- annotation_x, : annotation_z, } } -- OR Create struct: imperative style (start with base struct or EMPTY) local MyStruct = xtypes.struct{ MyStruct = { <optional base struct> } | xtypes.EMPTY } -- Set the i-th member: MyStruct[i] = { new_role = { new_xtemplate, [new_array | new_sequence,] [new_annotation, ...] } } -- After setting the i-th member, the following post-condition holds: -- NOTE: also holds for roles defined in the base struct datatype MyStruct[role] == 'prefix.enclosing.scope.path.to.role' MyStruct(role) == <the role definition> -- Delete the i-th member: MyStruct[i] = nil -- Set base class: MyStruct[xtypes.BASE] =
YourStruct
-- defined elsewhere -- Get the number of members in the struct (not including base struct): print(#MyStruct) -- Get the i-th member: -- { -- role = {template, [array|sequence,] [annotation1, annotation2, ...] -- } local member = MyStruct[i] local role, roledef = next(member) print(role, table.unpack(roledef)) -- Get a member definition by role name. The return value is a table -- in the same format that is used to define a member role: -- { template, [array|sequence,] [annotation1, annotation2, ...] } local roledef = MyStruct(role) print(table.unpack(roledef)) -- Get the member role's value print(MyStruct[role]) -- an accessor string into some storage system -- Get the base class: print(MyStruct[xtypes.BASE]) -- Iterate over the model definition (ordered): -- NOTE: does NOT show the roles defined in the base struct datatype for i = 1, #MyStruct do local role, roledef = next(MyStruct[i]) print(role, table.unpack(roledef)) end -- Iterate over instance members and the indexes (unordered): -- NOTE: shows roles defined in the base struct datatype for role, value in pairs(MyStruct) do print(role, value, table.unpack(MyStruct(role))) end - decl
{[string]={[string]={...},{[string]={...},...}
- union (decl)
-
Create a union datatype.
Parameters:
- decl
{[string]={xtemplate,{caseDisc1,...caseDiscN,string]={...}},...}}
a table containing a union name mapped to a table as follows.
{ MyUnion = { xtemplate, { caseDisc11, ..., caseDisc1N, role1 = {...} }, { caseDisc21, ..., caseDisc2N, role2 = {...} }, ... { xtypes.EMPTY, [..., caseDiscN,] role = {...} } -- default } }
where the member definition for a role is,
{ role = { xtemplate, [array | sequence,] [annotation, ...] } }
and xtypes.EMPTY is treated as the built-in default discriminator.
Returns:
-
table
an union datatype template (
xtemplate
). The table is a map of the role names to flattened out strings that represent the path from the enclosing top-level union scope to the role. The string values may be be used to retrieve the field values from some storage system.Usage:
-- Create union: declarative style local MyUnion = xtypes.union{ MyUnion = { <discriminator atom or enum>, -- must be the 1st item { caseDiscriminator11, caseDiscriminator12, ... caseDiscriminator1N, role_1 = {xtemplate_1,[array_1|sequence_1,][annotation_1,...]} }, : { caseDiscriminatorM1, caseDiscriminatorM2, ... caseDiscriminatorMN, role_M = {xtemplate_M,[array_M|sequence_M,][annotation_M,...]} }, { xtypes.EMPTY, [..., caseDiscriminatorN, ] -- default role = { xtemplate,[array|sequence,][annotation,...]} -- OPTIONAL -- annotation_x, : annotation_z, } } -- OR Create union: imperative style (start with EMPTY) local MyUnion = xtypes.union{ MyUnion = { <discriminator atom or enum> } } -- Set the i-th case: -- { -- caseDiscriminator1, caseDiscriminator2, ... caseDiscriminatorN, -- role = { template, [array|sequence,] [annotation1, annotation2, ...] } -- } MyUnion[i] = { caseDiscriminator1, ... caseDiscriminatorN, role = { xtemplate, [array | sequence,] [annotation, ...] } }, -- After setting the i-th member, the following post-condition holds: MyUnion[role] == 'prefix.enclosing.scope.path.to.role' MyUnion(role) == <the role definition> -- Delete the i-th case: MyUnion[i] = nil -- Set the discriminator type definition: MyUnion[xtypes.SWITCH] = <discriminator atom or enum> -- After setting the discriminator, the following post-condition holds: MyUnion._d == '#' -- Get the number of cases in the union: print(#MyUnion) -- Get the i-th case: -- { -- caseDiscriminator1, caseDiscriminator2, ... caseDiscriminatorN, -- role = { template, [array|sequence,] [annotation1, annotation2, ...] } -- } local case = MyUnion[i] print(table.unpack(case)) local role, roledef = next(case, #case) print('\t', role, table.unpack(roledef)) -- Get a member definition by role name. The return value is a table -- in the same format that is used to define a member role: -- { template, [array|sequence,] [annotation1, annotation2, ...] } local roledef = MyUnion(role) print(table.unpack(roledef)) -- Get the member role's value print(MyUnion[role]) -- an accessor string into some storage system -- Get the discriminator type definition: print(MyUnion[xtypes.SWITCH]) -- Get the discriminator's value print(MyUnion._d) -- an accessor string into some storage system -- Get the currectly selected member's value. -- i.e. the member selected by current discriminator value,
MyUnion._d
print(MyUnion()) -- may benil
, e.g. when there is no default discriminator -- Iterate over the model definition (ordered): for i = 1, #MyUnion do local case = MyUnion[i] local role, roledef = next(case, #case) print(role, table.unpack(roledef), ':', table.unpack(case)) end -- Iterate over instance members and the indexes (unordered): for role, value in pairs(MyUnion) do print(role, value, table.unpack(MyUnion(role))) end - decl
{[string]={xtemplate,{caseDisc1,...caseDiscN,string]={...}},...}}
a table containing a union name mapped to a table as follows.
- typedef (decl)
-
Create a typedef
alias
datatype. Typedefs are aliases for underlying datatypes.Parameters:
- decl
{[string]={xtemplate,[array or sequence,][annotation,...]}}
a table
containing a typedef mapped to an array as follows.
{ MyTypedef = { xtemplate, [array | sequence,] [annotation, ...] } }
where
xtemplate
is the underlying type definition, optionally followed by and array or sequence qualifiers to specify the multiplicity, optionally followed by annotations.
Returns:
-
table
an typedef datatype template (
xtemplate
).Usage:
-- Create a typedef datatype: local MyTypedef = xtypes.typedef{ MyTypedef = {
xtemplate
, [array | sequence,] [annotation, ...] } } -- Retreive the typedef definition print(MyTypedef()) --xtemplate
, [array | sequence] -- Resolve a typedef to the underlying non-typedef datatype and a list -- of collection qualifiers print(xtypes.resolve(MyTypedef)) -- Example: typedef sequence<MyStruct> MyStructSeq local MyStructSeq = xtypes.typedef{ MyStructSeq = { xtypes.MyStruct, xtypes.sequence() } } print(MyStructSeq()) -- MyStruct @sequence(10) -- Example: typedef MyStruct MyStructArray[10][20] local MyStructArray = xtypes.typedef{ MyStructArray = { xtypes.MyStruct, xtypes.array(10, 20) } } print(MyStructArray()) -- MyStruct @array(10, 20) - decl
{[string]={xtemplate,[array or sequence,][annotation,...]}}
a table
containing a typedef mapped to an array as follows.
- const (decl)
-
Create a constant.
Parameters:
- decl {[string]={atom,value}} a table containing a constant name mapped to an array containing an atom and a value of the atom datatype. NOTE: this method will try to convert the value to the correct type, if not already so (for example, if the value is a string).
Returns:
-
table
a constant.
Usage:
-- Create a constant datatype local MY_CONST = xtypes.const{ MY_CONST = { atom, <const_value> } } -- Get the const value and the underlying atomic datatype print(MY_CONST()) -- value atom -- Examples: local MAXLEN = xtypes.const{ MAXLEN = { xtypes.short, 128 } } print(MAXLEN()) -- 128 short local PI = xtypes.const{ PI = { xtypes.double, 3.14 } } print(PI()) -- 3.14 double local MY_STRING = xtypes.const{ MY_STRING = { xtypes.string(128), "My String Constant" } } print(MY_STRING()) -- My String Constant string<128> -- Use a const datatype local MyStringSeq = xtypes.typedef{ MyStringSeq = { xtypes.string, xtypes.sequence(MAXLEN) } }
- module (decl)
-
Create a module namespace.
A module is an name-space for holding (enclosing) datatypes.
Parameters:
- decl
{[string]={xtemplate,...}
a table containing a module name
mapped to an array of datatypes (
xtemplate
)
Returns:
-
table
a module namespace.
The table is a map of the datatype names to
xtemplate
canonical instances.Usage:
-- Create module: declarative style local MyModule = xtypes.module{ MyModule = { xtypes.const{...}, : xtypes.enum{...}, : xtypes.struct{...}, : xtypes.union{...}, : xtypes.typedef{...}, : xtypes.module{...}, -- nested module name-space : } } -- Create module: imperative style (start with EMPTY) local MyModule = xtypes.module{ MyModule = xtypes.EMPTY } -- Get the i-th member: print(MyModule[i]) -- Set the i-th member: MyModule[i] =
xtemplate
-- After setting the i-th member, the following post-condition holds: MyModule.name ==xtemplate
-- where: name =xtemplate
[xtypes.NAME] -- Delete the i-th member: MyModule[i] = nil -- Get the number of members in the module: print(#MyModule) -- Iterate over the module definition (ordered): for i = 1, #MyModule do print(MyModule[i]) end -- Iterate over module namespace (unordered): for k, v in pairs(MyModule) do print(k, v) end - decl
{[string]={xtemplate,...}
a table containing a module name
mapped to an array of datatypes (
Builtin Datatypes
- boolean
- boolean.
- octet
- octet.
- char
- char.
- wchar
- wide char.
- float
- float.
- double
- double.
- long_double
- long double.
- short
- short.
- long
- long.
- long_long
- long long.
- unsigned_short
- unsigned short.
- unsigned_long
- unsigned long.
- unsigned_long_long
- unsigned long long.
- string (n)
-
string<n>
: string of length n.Parameters:
- n int the maximum length of the string
Returns:
-
xtemplate
the string datatype
- wstring (n)
-
wstring<n>
:wstring of length n.Parameters:
- n int the maximum length of the wstring
Returns:
-
xtemplate
the wstring datatype
Builtin Annotations
- Key
-
Datatype member is a key field.
@Key
- ID
-
Datatype member id.
@ID{n}
- Optional
-
Datatype member is optional.
@Optional
- MustUnderstand
-
Datatype member is required.
@MustUnderstand
- Shared
-
Datatype member is shared.
@Shared
- BitBound
-
enum datatype is bit-bound.
@BitBound{n}
- BitSet
-
enum datatype is a bit-set.
@BitSet
- Extensibility
-
Datatype extensibility.
@Extensibility{'EXTENSIBLE_EXTENSIBILITY'|'MUTABLE_EXTENSIBILITY'|'FINAL_EXTENSIBILITY}
- Nested
-
Datatype is not top-level it is nexted.
@Nested
- top_level
-
Datatype may (or may not) be top-level.
@top_level{false}