Advanced syntax
Method declarations
A method can be declared indented under a model prefixed with the fn keyword and potentially the static keyword as well, in which case it is put before fn, i.e. static fn. The fn keyword is followed by the name of the method followed by a parameter list. After the parameter list follows an optional return value consisting of a right arrow -> and the return type.
The parameter list is wrapped with parenthesis, (), and each parameter is separated by a comma ,. Each parameter is defined by a name followed by : and a type.
Foo:
x is Real: 1.0
ModelWithMethods:
fn my_method(x: Real, y: Foo, z: Bool[]) -> Int
static fn my_static_method() -> Foo
# Methods without return types are special and cannot be called from within OpenPLX
static fn my_void_method(foo: Foo)
fn onInit()
Operator overloads
To declare an operator overload use the operator keyword followed by the operator to overload, +, -, * or / followed by a parameter list.
Foo:
x is Real: 1.0
Bar:
y is Int: 2.0
operator +(lhs: Foo, rhs: Foo) -> Foo
operator -(unary: Foo) -> Foo
operator -(lhs: Foo, rhs: Bar) -> Foo
operator *(lhs: Bar, rhs: Foo) -> Bar
operator /(lhs: Foo, rhs: Foo) -> Bar
Union types
OpenPLX supports basic union types, where you can declare an attribute that can take any of several types. The union types can be narrowed to a specific type using the becomes keyword.
ModelA:
x is Int: 1
ModelB:
y is Int: 2
ModelWithUnionType:
a_or_b is ModelA or ModelB
ModelSpecializingUnionType is ModelWithUnionType:
a_or_b becomes ModelB
References
By default, an attribute in OpenPLX is owned by the object that declares it. In other words, the attribute usually represents a newly created instance of the specified type.
However, an attribute may instead refer to an existing instance. This is done by assigning the attribute’s right-hand side (RHS) to a previously defined object. In the example below, bar2 becomes a reference to the instance bar:
Bar
Foo:
bar is Bar
bar2 is Bar: bar
While this is valid, the intention can sometimes be unclear from the code alone. Many models rely on attributes that are expected to reference existing objects rather than create new ones.
When the reference keyword is used, it also enforces that the attribute must be assigned explicitly. If a model author forgets to set such an attribute, the absence will be reported as an error. Without the reference keyword, however, an attribute that was intended to reference an existing object might still be considered valid even if left unset—creating a subtle risk that a required specification is silently missing.
Why References Matter
Some attributes are designed to point to previously declared instances. For example:
Physics3D.System.reference_bodyidentifies an already-declared body that the system should treat as fixed relative to its local transform—unless the model author specifies something else.- Interaction models in
Physics.Interactionsexpect references to connectors that are owned by bodies or systems.
In such cases, it is important to use the reference keyword to make the intention explicit: the attribute must reference an existing instance of the specified type.
Declaring Explicit References
To clearly state that an attribute is a reference, use the reference keyword:
Bar
Foo:
bar is Bar
bar2 is Bar reference: bar
This makes it clear that bar2 references an existing Bar instance, and that bar is the instance being referenced.
Example Usage
The reference keyword is common in the standard library to express object relationships. For example:
Vehicles.Suspensions.Suspension:
chassis_connector is Vehicles.Suspensions.SuspensionConnector reference
attachment_connector is Physics3D.Interactions.MateConnector reference
connectors: [chassis_connector, attachment_connector]
Here, both connectors are required to reference existing connector instances. The connectors list then collects those referenced connectors.