-
Notifications
You must be signed in to change notification settings - Fork 0
Associations
Here is an example of a one-to-many association relationship.
R1
has service features define by, 1 Bank
defines service features of, M Shaft
Shaft.Bank -> Bank.Name
--
It might be helpful to see this in diagram form:

After the R1 rname we have two lines that each define a phrase and multiplicity on one direction of the
association. This if followed by one line for each attribute reference, which turns out to be just one in
this case. Here's the grammar:
// Binary association
binary_rel = t_side p_side assoc_class? ref1 ref2? // Both sides
t_side = rel_side
p_side = rel_side
rel_side = INDENT phrase "," SP mult SP+ icaps_name EOL* // One side of a binary association
assoc_class = INDENT ('1' / 'M') SP+ icaps_name EOL* // Association class
ref1 = binref
ref2 = binref
...
phrase = lword (delim lword)* // Relationship phrase
lword = r'[a-z][a-z]*' // Lower case only word (used in relationship phrase)
The two sides of the association are represented by the t_side and p_side tokens, both of which has the same
structure. This is a verb phrase (any sequence of lower case words), followed by a comma and a single space.
After that, on the same line, you have the multiplicity. Here's the grammar for that:
mult = r'[1M]c?' // Binary relationship multiplicity, Shlaer-Mellor notation
That's either a 1 or M cardinality followed by a c if it is conditional (zero permitted).
So a UML 0..1 would be 1c (one conditional), and * would be Mc.
The multiplicity is followed by a single space and then you have the icaps_name of the target class.
The final line in the R1 example defines each referential attribute, one in this case, that formalizes the
association. This is a binref token with the following grammar:
binref = INDENT source_attrs SP '->' (WRAP / SP) target_attrs reflex? EOL* // Single class to single class ref
reflex = '.' phrase
//binref = INDENT source_attrs SP '->' (WRAP / SP) target_attrs (',' SP itag)? EOL* // Single class to single class ref
source_attrs = single_class_attrs // So parse visitor can define distinct source target dict keys
target_attrs = single_class_attrs
single_class_attrs = class_name attr_set // A subset of attrs from a single class
attr_set = '.' ('(' attr_name (',' SP attr_name)+ ')' / attr_name) // The subset attr names or name
Wow, that seems like a lot of grammar for this simple line:
Shaft.Bank -> Bank.Name
That's because the R1 association is a simple case. Let's break it down before moving on to a more involved
example.
The text on the left side of the -> keyword is the referring attribute set which happens in our case to just
be a single attribute: Shaft.Bank. The same is true of the referenced attribute on the right side.
So the line is simply saying that the Shaft.Bank attribute references the value of the Bank.Name attribute.
In other words, any value of Shaft.Bank must match a value of Bank.Name.
Here is an association formalization (binref) that involves multiple attributes.
R43
is location of, 1c Cabin
is at, 1 Shaft Level
Cabin.(Shaft, Current floor) -> Shaft Level.(Shaft, Floor)
Here you need both a Shaft and Floor to uniquely identify a Shaft Level. So the Cabin's Shaft and Current floor attributes reference the Shaft Level's Shaft and Floor attributes. The modeler must ensure that the corresponding types match, but that detail is not checked by the class model parser.
Consequently, the ordering of attribute names in each pair of parentheses must match. If it doesn't, the downstream population module will have a word with you when it tries to load your model.
Copyright 2023-2026 © Leon Starr under MIT Open Source License