All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fulib.scenarios.visitor.resolve.ConstraintResolver Maven / Gradle / Ivy

package org.fulib.scenarios.visitor.resolve;

import org.fulib.scenarios.ast.decl.Decl;
import org.fulib.scenarios.ast.decl.Name;
import org.fulib.scenarios.ast.decl.ResolvedName;
import org.fulib.scenarios.ast.decl.VarDecl;
import org.fulib.scenarios.ast.expr.Expr;
import org.fulib.scenarios.ast.expr.conditional.ConditionalOperator;
import org.fulib.scenarios.ast.expr.conditional.ConditionalOperatorExpr;
import org.fulib.scenarios.ast.expr.primary.NameAccess;
import org.fulib.scenarios.ast.pattern.*;
import org.fulib.scenarios.ast.scope.PatternReferenceCollectingScope;
import org.fulib.scenarios.ast.scope.Scope;
import org.fulib.scenarios.ast.type.PrimitiveType;
import org.fulib.scenarios.ast.type.Type;
import org.fulib.scenarios.diagnostic.Marker;
import org.fulib.scenarios.diagnostic.Position;

import java.util.*;

public class ConstraintResolver implements Constraint.Visitor
{
   private int uniqueIndex;

   @Override
   public Constraint visit(AndConstraint andConstraint, Scope par)
   {
      andConstraint.getConstraints().replaceAll(c -> {
         c.setOwner(andConstraint.getOwner());
         return c.accept(this, par);
      });

      if (andConstraint.getConstraints().size() == 1)
      {
         return andConstraint.getConstraints().get(0);
      }
      return andConstraint;
   }

   @Override
   public Constraint visit(LinkConstraint linkConstraint, Scope par)
   {
      final Name target = linkConstraint.getTarget();
      final Name resolvedTarget = target.accept(NameResolver.INSTANCE, par);
      linkConstraint.setTarget(resolvedTarget);

      final Decl decl = resolvedTarget.getDecl();
      if (decl == null)
      {
         par.report(Marker.error(target.getPosition(), "link-constraint.target.unresolved", target.getValue()));
      }
      else if (!(decl instanceof VarDecl) || ((VarDecl) decl).getPattern() == null)
      {
         par.report(Marker.error(target.getPosition(), "link-constraint.target.not.pattern-object", target.getValue()));
      }

      return linkConstraint;
   }

   @Override
   public Constraint visit(AttributeConstraint attributeConstraint, Scope par)
   {
      if (attributeConstraint.getPattern() == null)
      {
         this.makePattern(attributeConstraint, PrimitiveType.OBJECT);
      }
      return attributeConstraint;
   }

   private void makePattern(AttributeConstraint ac, Type type)
   {
      final Pattern owner = ac.getOwner();
      final Name attribute = ac.getAttribute();
      final String attributeName = attribute == null ? "Attr" + ++this.uniqueIndex : attribute.getValue();
      final VarDecl varDecl = VarDecl.of(owner.getName().getValue() + attributeName, type, null);
      final Pattern pattern = Pattern.of(type, ResolvedName.of(varDecl), Collections.emptyList());
      ac.setPattern(pattern);
   }

   static Pattern getPatternIfRef(Expr expr)
   {
      if (!(expr instanceof NameAccess))
      {
         return null;
      }

      final NameAccess access = (NameAccess) expr;
      final Name targetName = access.getName();
      final Decl decl = targetName.getDecl();

      if (!(decl instanceof VarDecl))
      {
         return null;
      }

      final VarDecl varDecl = (VarDecl) decl;
      return varDecl.getPattern();
   }

   @Override
   public Constraint visit(AttributeEqualityConstraint aec, Scope par)
   {
      final Expr expr = aec.getExpr().accept(ExprResolver.INSTANCE, par);

      // We match some object foo and some object bar
      // where attr of foo is bar.

      final Pattern target = getPatternIfRef(expr);
      if (target != null)
      {
         final LinkConstraint lc = LinkConstraint.of(aec.getAttribute(), target.getName());
         lc.setOwner(aec.getOwner());
         lc.setPosition(aec.getPosition());
         return lc.accept(this, par);
      }

      aec.setExpr(expr);

      this.makePattern(aec, expr.getType());

      return aec;
   }

   @Override
   public Constraint visit(AttributePredicateConstraint attributePredicateConstraint, Scope par)
   {
      // TODO disabled in parser, because the only predicates that exist are for lists or null,
      //      both of which never appear in tables
      return attributePredicateConstraint;
   }

   @Override
   public Constraint visit(AttributeConditionalConstraint acc, Scope par)
   {
      final Pattern owner = acc.getOwner();
      final Position position = acc.getPosition();
      final Name attribute = acc.getAttribute();
      final Expr rhs = acc.getRhs();
      ConditionalOperator operator = acc.getOperator();

      switch (operator)
      {
      case NOT_CONTAINS:
         par.report(Marker.error(acc.getPosition(), "attribute-constraint.conditional.not-contains"));
         break;
      // pattern matching always flattens lists, so contains is never useful and can be replaced with is.
      case CONTAINS:
      case IS:
         final AttributeEqualityConstraint aec = AttributeEqualityConstraint.of(attribute, rhs);
         aec.setOwner(owner);
         aec.setPosition(position);
         return aec.accept(this, par);
      }

      final Set patterns = new HashSet<>();
      final Scope scope = new PatternReferenceCollectingScope(par, patterns);
      final Expr resolvedRhs = rhs.accept(ExprResolver.INSTANCE, scope);
      final Type nullableLhsType = operator.getLhsType();
      final Type lhsType = nullableLhsType != null ? nullableLhsType : PrimitiveType.OBJECT;

      if (patterns.isEmpty())
      {
         acc.setRhs(resolvedRhs);

         this.makePattern(acc, lhsType);
         return acc;
      }

      // the rhs references a pattern object
      // -> convert to a generic attribute constraint followed by a match constraint

      final AttributeConstraint ac = AttributeConstraint.of(attribute);
      ac.setOwner(owner);
      ac.setPosition(position);
      this.makePattern(ac, lhsType);

      final Expr lhs = NameAccess.of(ac.getPattern().getName());
      lhs.setPosition(position);

      final Expr condExpr = ConditionalOperatorExpr.of(lhs, operator, resolvedRhs).accept(ExprResolver.INSTANCE, par);
      condExpr.setPosition(position);

      final MatchConstraint matchConstraint = MatchConstraint.of(condExpr, prepend(ac.getPattern(), patterns));
      matchConstraint.setOwner(owner);
      matchConstraint.setPosition(position);

      final AndConstraint andConstraint = AndConstraint.of(Arrays.asList(ac, matchConstraint));
      andConstraint.setOwner(owner);
      andConstraint.setPosition(position);
      return andConstraint;
   }

   private static List prepend(Pattern pattern, Collection patterns)
   {
      final List result = new ArrayList<>(1 + patterns.size());
      result.add(pattern);
      result.addAll(patterns);
      return result;
   }

   @Override
   public Constraint visit(MatchConstraint matchConstraint, Scope par)
   {
      final Set patterns = new HashSet<>();
      final Scope scope = new PatternReferenceCollectingScope(par, patterns);

      matchConstraint.setExpr(matchConstraint.getExpr().accept(ExprResolver.INSTANCE, scope));
      matchConstraint.getPatterns().addAll(patterns);
      return matchConstraint;
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy