@@ -157,8 +157,8 @@ public Void visitLetRecDefinition(LetRecDefinitionContext ctx) {
157157 final int recSize = ctx .letRecAndDefinition ().size () + 1 ;
158158 final Relation [] recursiveGroup = new Relation [recSize ];
159159 final String [] lhsNames = new String [recSize ];
160-
161- // Create the recursive relations
160+ final ExpressionContext [] rhsContexts = new ExpressionContext [ recSize ];
161+ // Recompose the syntax.
162162 for (int i = 0 ; i < recSize ; i ++) {
163163 // First relation needs special treatment due to syntactic difference
164164 final String relName = i == 0 ? ctx .n .getText () : ctx .letRecAndDefinition (i - 1 ).n .getText ();
@@ -167,14 +167,18 @@ public Void visitLetRecDefinition(LetRecDefinitionContext ctx) {
167167 throw new MalformedMemoryModelException (errorMsg );
168168 }
169169 lhsNames [i ] = relName ;
170- recursiveGroup [i ] = wmm .newRelation (createUniqueName (relName ));
170+ rhsContexts [i ] = i == 0 ? ctx .e : ctx .letRecAndDefinition (i - 1 ).e ;
171+ }
172+ // Create the recursive relations.
173+ for (int i = 0 ; i < recSize ; i ++) {
174+ final Relation .Arity arity = rhsContexts [i ].accept (new ArityInspector ());
175+ recursiveGroup [i ] = wmm .newRelation (createUniqueName (lhsNames [i ]), arity );
171176 recursiveGroup [i ].setRecursive ();
172- namespace .put (relName , recursiveGroup [i ]);
177+ namespace .put (lhsNames [ i ] , recursiveGroup [i ]);
173178 }
174-
175- // Parse recursive definitions
179+ // Parse recursive definitions.
176180 for (int i = 0 ; i < recSize ; i ++) {
177- final ExpressionContext rhs = i == 0 ? ctx . e : ctx . letRecAndDefinition ( i - 1 ). e ;
181+ final ExpressionContext rhs = rhsContexts [ i ] ;
178182 final Relation lhs = recursiveGroup [i ];
179183 setRelationToBeDefined (lhs );
180184 final Relation parsedRhs = parseAsRelation (rhs );
@@ -326,15 +330,13 @@ public Relation visitExprTransRef(ExprTransRefContext c) {
326330
327331 @ Override
328332 public Relation visitExprDomain (ExprDomainContext c ) {
329- checkNoRecursion (c );
330333 Relation r0 = wmm .newSet ();
331334 Relation r1 = parseAsRelation (c .e );
332335 return addDefinition (new Domain (r0 , r1 ));
333336 }
334337
335338 @ Override
336339 public Relation visitExprRange (ExprRangeContext c ) {
337- checkNoRecursion (c );
338340 Relation r0 = wmm .newSet ();
339341 Relation r1 = parseAsRelation (c .e );
340342 return addDefinition (new Range (r0 , r1 ));
@@ -407,5 +409,69 @@ private static CatParser getParser(CharStream input) {
407409 parser .addErrorListener (new DiagnosticErrorListener (true ));
408410 return parser ;
409411 }
412+
413+ private final class ArityInspector extends CatBaseVisitor <Relation .Arity > {
414+
415+ private ArityInspector () {}
416+
417+ @ Override
418+ public Relation .Arity visitExprBasic (ExprBasicContext c ) {
419+ final Object object = namespace .get (c .n .getText ());
420+ return object instanceof Relation r ? r .getArity () : null ;
421+ }
422+
423+ @ Override
424+ public Relation .Arity visitExprDomain (ExprDomainContext c ) {
425+ return Relation .Arity .UNARY ;
426+ }
427+
428+ @ Override
429+ public Relation .Arity visitExprRange (ExprRangeContext c ) {
430+ return Relation .Arity .UNARY ;
431+ }
432+
433+ @ Override
434+ public Relation .Arity visitExprCartesian (ExprCartesianContext c ) {
435+ return Relation .Arity .BINARY ;
436+ }
437+
438+ @ Override
439+ public Relation .Arity visitExprComposition (ExprCompositionContext c ) {
440+ return Relation .Arity .BINARY ;
441+ }
442+
443+ @ Override
444+ public Relation .Arity visitExprIdentity (ExprIdentityContext c ) {
445+ return Relation .Arity .BINARY ;
446+ }
447+
448+ @ Override
449+ public Relation .Arity visitExprUnion (ExprUnionContext c ) {
450+ return join (c .e1 , c .e2 );
451+ }
452+
453+ @ Override
454+ public Relation .Arity visitExprIntersection (ExprIntersectionContext c ) {
455+ return join (c .e1 , c .e2 );
456+ }
457+
458+ @ Override
459+ public Relation .Arity visitExprMinus (ExprMinusContext c ) {
460+ return join (c .e1 , c .e2 );
461+ }
462+
463+ private Relation .Arity join (ExpressionContext e1 , ExpressionContext e2 ) {
464+ final Relation .Arity k1 = e1 .accept (this );
465+ final Relation .Arity k2 = e2 .accept (this );
466+ checkCompatible (k1 , k2 );
467+ return k1 == null ? k2 : k1 ;
468+ }
469+
470+ private static void checkCompatible (Relation .Arity k1 , Relation .Arity k2 ) {
471+ if (k1 != null && k2 != null && !k1 .equals (k2 )) {
472+ throw new ParsingException ("Incompatible kinds %s and %s" .formatted (k1 , k2 ));
473+ }
474+ }
475+ }
410476}
411477
0 commit comments