Tipos de schema UML en Nereus

Visibility (4 niveles), Multiplicity (4 constructores) y una RELATION SCHEMA ParticipatesSchema que combina ambos sobre el caso Person × Meeting.
Validación: las tres specs pasan el analyzer con 0 errores. Solo queda 1 warning V1 sobre schemaConforms (operación derivada con LHS de variable libre — falso positivo conocido del workspace).
Decisiones de diseño (acordadas):

1. Visibility — 4 niveles UML

Visibility.nereus
CLASS Visibility
BASIC CONSTRUCTORS Vis_Public, Vis_Private, Vis_Protected, Vis_Package
EFFECTIVE
TYPE Visibility

OPERATIONS
  Vis_Public:    -> Visibility;
  Vis_Private:   -> Visibility;
  Vis_Protected: -> Visibility;
  Vis_Package:   -> Visibility;

  vis_symbol:       Visibility -> String;
  vis_isAccessible: Visibility * Visibility -> Boolean;

AXIOMS  v: Visibility;

  -- Rendering UML.
  vis_symbol(Vis_Public())    = "+";
  vis_symbol(Vis_Private())   = "-";
  vis_symbol(Vis_Protected()) = "#";
  vis_symbol(Vis_Package())   = "~";

  -- Regla de accesibilidad: vis_isAccessible(callerVis, declaredVis).
  vis_isAccessible(v, Vis_Public())    = True;
  vis_isAccessible(v, Vis_Private())   = False;
  vis_isAccessible(v, Vis_Protected()) = (v = Vis_Protected()) or (v = Vis_Private());
  vis_isAccessible(v, Vis_Package())   = (v = Vis_Package())   or (v = Vis_Private());

END-CLASS

Tabla UML → Nereus

UML symbolNombre UMLConstructor NereusSignificado
+PublicVis_Public()Accesible desde cualquier parte.
-PrivateVis_Private()Solo accesible internamente.
#ProtectedVis_Protected()Accesible por subclases.
~PackageVis_Package()Accesible dentro del paquete.

2. Multiplicity — 4 constructores algebraicos

Multiplicity.nereus
CLASS Multiplicity
BASIC CONSTRUCTORS Mult_Exact, Mult_Range, Mult_AtLeast, Mult_Star
EFFECTIVE
TYPE Multiplicity

OPERATIONS
  Mult_Star:    -> Multiplicity;
  Mult_AtLeast: Integer -> Multiplicity;
  Mult_Range:   Integer * Integer -> Multiplicity;
  Mult_Exact:   Integer -> Multiplicity;

  mult_lower:   Multiplicity -> Integer;
  mult_upper:   Multiplicity -> Integer;  -- (0 - 1) = ∞
  mult_isStar:  Multiplicity -> Boolean;
  mult_accepts: Multiplicity * Integer -> Boolean;

AXIOMS  n, m, k: Integer;

  -- Cotas inferiores.
  mult_lower(Mult_Star())      = 0;
  mult_lower(Mult_AtLeast(n))  = n;
  mult_lower(Mult_Range(n, m)) = n;
  mult_lower(Mult_Exact(n))    = n;

  -- Cotas superiores. (0 - 1) representa ∞.
  mult_upper(Mult_Star())      = 0 - 1;
  mult_upper(Mult_AtLeast(n))  = 0 - 1;
  mult_upper(Mult_Range(n, m)) = m;
  mult_upper(Mult_Exact(n))    = n;

  mult_isStar(Mult_Star())     = True;
  mult_isStar(Mult_AtLeast(n)) = True;
  mult_isStar(Mult_Range(n, m))= False;
  mult_isStar(Mult_Exact(n))   = False;

  -- Pertenencia: ¿k cae dentro del rango?
  mult_accepts(Mult_Star(),      k) = (k >= 0);
  mult_accepts(Mult_AtLeast(n),  k) = (k >= n);
  mult_accepts(Mult_Range(n, m), k) = (n <= k) and (k <= m);
  mult_accepts(Mult_Exact(n),    k) = (k = n);

END-CLASS

Tabla UML → Nereus

UML notationSignificadoConstructor Nereuslower / upper
*0..∞ (cero o más)Mult_Star()0 / ∞
0..*0..∞ (idem)Mult_Star() o Mult_AtLeast(0)0 / ∞
1..*uno o másMult_AtLeast(1)1 / ∞
n..*n o másMult_AtLeast(n)n / ∞
n..mentre n y mMult_Range(n, m)n / m
nexactamente nMult_Exact(n)n / n
0..1opcionalMult_Range(0, 1)0 / 1
1obligatorio simpleMult_Exact(1)1 / 1

3. ParticipatesSchema — RELATION SCHEMA Person × Meeting

Esta spec aplica los dos tipos anteriores al caso Person&Meeting del libro. Mantiene los axiomas algebraicos estándar (create, addLink, isRelated, get_meetings, get_participants, etc.) y agrega metadata de schema que se observa con operaciones a nivel-asociación: role1/role2, mult1/mult2, visibility1/visibility2, isFrozen/isChangeable/isAddOnly.

ParticipatesSchema.nereus (extracto)
RELATION SCHEMA ParticipatesSchema
IMPORTS Person, Meeting, Multiplicity, Visibility,
        Set[Person:ANY], Set[Meeting:ANY]
BASIC CONSTRUCTORS create, addLink
EFFECTIVE
TYPE ParticipatesSchema

OPERATIONS

  -- Constructores algebraicos.
  create:  -> ParticipatesSchema;
  addLink: ParticipatesSchema(a) * Person(p) * Meeting(m)
           -> ParticipatesSchema
    pre: not isRelated(a, p, m);

  -- Metadata del schema.
  role1, role2:        ParticipatesSchema -> String;
  mult1, mult2:        ParticipatesSchema -> Multiplicity;
  visibility1, visibility2: ParticipatesSchema -> Visibility;
  isFrozen, isChangeable, isAddOnly: ParticipatesSchema -> Boolean;

  -- Predicados y cardinalidades observadas.
  isRelated:        ParticipatesSchema * Person * Meeting -> Boolean;
  isRightLinked:    ParticipatesSchema * Person -> Boolean;
  isLeftLinked:     ParticipatesSchema * Meeting -> Boolean;
  rightCardinality: ParticipatesSchema * Person -> Integer;
  leftCardinality:  ParticipatesSchema * Meeting -> Integer;

  -- Navegación.
  get_meetings:     ParticipatesSchema * Person -> Set[Meeting];
  get_participants: ParticipatesSchema * Meeting -> Set[Person];

  -- Remover y verificar conformidad al schema.
  remove: ParticipatesSchema(a) * Person(p) * Meeting(m)
          -> ParticipatesSchema
    pre: isRelated(a, p, m) and not isFrozen(a) and not isAddOnly(a);
  schemaConforms: ParticipatesSchema * Person * Meeting -> Boolean;

AXIOMS  a: ParticipatesSchema;  p, p1: Person;  m, m1: Meeting;

  -- ─── Metadata del schema (constantes por instancia) ──
  role1(create())         = "participant";
  role1(addLink(a, p, m)) = role1(a);

  role2(create())         = "scheduled_meeting";
  role2(addLink(a, p, m)) = role2(a);

  mult1(create())         = Mult_AtLeast(1);   -- 1..*
  mult1(addLink(a, p, m)) = mult1(a);

  mult2(create())         = Mult_Star();        -- 0..*
  mult2(addLink(a, p, m)) = mult2(a);

  visibility1(create())         = Vis_Public();
  visibility1(addLink(a, p, m)) = visibility1(a);

  visibility2(create())         = Vis_Public();
  visibility2(addLink(a, p, m)) = visibility2(a);

  isFrozen(create())            = False;
  isFrozen(addLink(a, p, m))    = isFrozen(a);

  isChangeable(create())        = True;
  isChangeable(addLink(a, p, m))= isChangeable(a);

  isAddOnly(create())           = False;
  isAddOnly(addLink(a, p, m))   = isAddOnly(a);

  -- ─── Conformidad al schema ────────────────────────────
  -- schemaConforms(a, p, m): la cardinalidad observada de p
  -- en a cumple mult2, y la de m en a cumple mult1.
  schemaConforms(a, p, m) =
      mult_accepts(mult2(a), rightCardinality(a, p))
      and
      mult_accepts(mult1(a), leftCardinality(a, m));

  -- (Los axiomas de isRelated, get_meetings, get_participants,
  --  cardinalidades, remove, etc. siguen el patrón estándar
  --  de ASSOCIATION; ver el archivo completo.)

END-CLASS

4. Ejemplo concreto: tres Persons, dos Meetings, tres links

Para ilustrar cómo se usa schemaConforms, simulo una sesión:

ConstrucciónValor algebraicoVisión semántica
Empty a0 = create() {}
Insert (Alice, M1) a1 = addLink(a0, Alice, M1) { (Alice, M1) }
Insert (Bob, M1) a2 = addLink(a1, Bob, M1) { (Alice, M1), (Bob, M1) }
Insert (Alice, M2) a3 = addLink(a2, Alice, M2) { (Alice, M1), (Bob, M1), (Alice, M2) }

Evaluación de metadata y conformidad sobre a3

ExpresiónResultadoPor qué
role1(a3) "participant" Constante propagada por axiomas; no depende de los links.
mult1(a3) Mult_AtLeast(1) Constante. Cada reunión necesita ≥1 participante.
mult2(a3) Mult_Star() Constante. Una persona puede tener 0 o más reuniones.
visibility1(a3) Vis_Public() Constante. Se puede leer la asociación libremente.
rightCardinality(a3, Alice) 2 Alice está en M1 y M2.
rightCardinality(a3, Bob) 1 Bob solo en M1.
leftCardinality(a3, M1) 2 M1 tiene a Alice y Bob.
leftCardinality(a3, M2) 1 M2 solo tiene a Alice.
mult_accepts(Mult_AtLeast(1), 2) True M1 cumple mult1 (≥1 participante). ✓
mult_accepts(Mult_Star(), 2) True Alice cumple mult2 (0..*). ✓
schemaConforms(a3, Alice, M1) True Ambas cardinalidades dentro del rango. ✓
schemaConforms(a3, Alice, M2) True 1 ∈ [1..∞], 2 ∈ [0..∞]. ✓

Caso de violación: agregar (Carol, M3) donde M3 no tiene a nadie aún

Si hubiéramos construido a4 = addLink(create(), Carol, M3) directamente (sin más links), la cardinalidad izquierda de M3 es 1, lo que cumple mult1 = 1..*. Pero si por algún motivo se removiera ese único link (remove(a4, Carol, M3)) y M3 quedara con 0 participantes, leftCardinality daría 0, y mult_accepts(Mult_AtLeast(1), 0) = False — el schema dejaría de conformar. Ese chequeo se hace en runtime con schemaConforms.

5. Limitaciones del parser detectadas

1. Literales enteros negativos no soportados. -1 falla en el parser; usamos 0 - 1 como workaround para la convención de "cota superior infinita". El significado es el mismo (el evaluador computa la resta antes de comparar).
2. END-RELATION no reconocido. Aunque el parser sí acepta el header RELATION SCHEMA ..., no acepta el cierre simétrico END-RELATION. Como workaround, las specs RELATION SCHEMA deben cerrar con END-CLASS. Esta es una asimetría sintáctica del analyzer que vale la pena reportar — la misma asimetría aparece en Bidirectional_3.nereus con el mismo error.
3. Concatenación de strings (+ sobre String). No está soportada. mult_render(Mult_Range(1, 5)) = intToString(1) + ".." + intToString(5) falla. Por eso eliminé mult_render: el rendering UML se hace en el target (Haxe/C++/etc.) si hace falta.

6. Mapeo a códigos objetivo

Tipo Nereus Haxe C++ Java
Visibility (4 constructores nullary) enum Visibility { Vis_Public; Vis_Private; Vis_Protected; Vis_Package; } enum class Visibility { Public, Private, Protected, Package }; enum Visibility { PUBLIC, PRIVATE, PROTECTED, PACKAGE }
Multiplicity (4 constructores, paramétricos) enum Multiplicity { Mult_Star; Mult_AtLeast(n:Int); Mult_Range(n:Int, m:Int); Mult_Exact(n:Int); } (GADT) std::variant<MultStar, MultAtLeast, MultRange, MultExact> o jerarquía sellada Sealed class jerarquía: sealed class Multiplicity permits MultStar, MultAtLeast, MultRange, MultExact {}