+package org.simantics.scl.compiler.elaboration.chr.ast;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.elaboration.chr.CHRLiteral;
+import org.simantics.scl.compiler.elaboration.chr.relations.SpecialCHRRelation;
+import org.simantics.scl.compiler.elaboration.chr.relations.UnresolvedCHRRelation;
+import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
+import org.simantics.scl.compiler.elaboration.expressions.EApply;
+import org.simantics.scl.compiler.elaboration.expressions.EBinary;
+import org.simantics.scl.compiler.elaboration.expressions.EConstant;
+import org.simantics.scl.compiler.elaboration.expressions.ERecord;
+import org.simantics.scl.compiler.elaboration.expressions.EVar;
+import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
+import org.simantics.scl.compiler.environment.AmbiguousNameException;
+import org.simantics.scl.compiler.environment.Environments;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.compiler.types.Types;
+
+public class CHRAstAtom extends CHRAstQuery {
+ public Expression expression;
+ public boolean remove;
+
+ public CHRAstAtom(Expression expression, boolean remove) {
+ this.expression = expression;
+ this.remove = remove;
+ }
+
+ @Override
+ public void accept(CHRAstQueryVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ public static CHRAstQuery atom(Expression expression) {
+ boolean remove = false;
+ if(expression instanceof EVar) {
+ EVar var = (EVar)expression;
+ if(var.name.equals("True")) {
+ CHRAstConjunction query = new CHRAstConjunction(Collections.emptyList());
+ query.location = expression.location;
+ return query;
+ }
+ }
+ else if(expression instanceof EBinary) {
+ EBinary binary = (EBinary)expression;
+ if(binary.negation != null && binary.rights.isEmpty()) {
+ remove = true;
+ expression = binary.left;
+ }
+ // If query is marked for removal, it must be an atom
+ }
+ else if(expression instanceof EApply) {
+ EApply apply = (EApply)expression;
+ if(apply.function instanceof EVar && ((EVar)apply.function).name.equals("not")) {
+ Expression subExpression;
+ if(apply.parameters.length == 1)
+ subExpression = apply.parameters[0];
+ else
+ subExpression = new EApply(
+ Locations.combine(apply.parameters[0].location, apply.parameters[apply.parameters.length-1].location),
+ apply.parameters[0],
+ Arrays.copyOfRange(apply.parameters, 1, apply.parameters.length));
+ CHRAstNegation query = new CHRAstNegation(atom(subExpression));
+ query.location = expression.location;
+ return query;
+ }
+ else if(apply.function instanceof EConstant) {
+ Name valueName = ((EConstant)apply.function).getValue().getName();
+ if(valueName.module.equals(Types.BUILTIN) && valueName.name.startsWith("(")) {
+ CHRAstQuery[] conjuncts = new CHRAstQuery[apply.parameters.length];
+ for(int i=0;i<conjuncts.length;++i)
+ conjuncts[i] = atom(apply.parameters[i]);
+ CHRAstQuery query = CHRAstConjunction.conjunction(conjuncts);
+ query.location = expression.location;
+ return query;
+ }
+ }
+ }
+ CHRAstAtom query = new CHRAstAtom(expression, remove);
+ query.location = expression.location;
+ return query;
+ }
+
+ @Override
+ protected void translate(TranslationContext context, boolean isHead, ArrayList<CHRLiteral> literals) {
+ literals.add(
+ isConstraint(context, expression) ?
+ convertConstraint(remove, expression) :
+ convertExpression(isHead, expression));
+ }
+
+ private static boolean isConstraint(TranslationContext context, Expression expression) {
+ if(expression instanceof EApply)
+ expression = ((EApply)expression).function;
+ else if(expression instanceof ERecord)
+ expression = ((ERecord)expression).constructor;
+ if(!(expression instanceof EVar))
+ return false;
+ String name = ((EVar)expression).name;
+ if(TranslationContext.isConstructorName(name))
+ return true;
+ try {
+ return Environments.getRelation(context.getEnvironment(), name) != null;
+ } catch (AmbiguousNameException e) {
+ return true;
+ }
+ }
+
+ private static CHRLiteral convertExpression(boolean isHead, Expression expression) {
+ if(isHead)
+ return new CHRLiteral(expression.location, SpecialCHRRelation.CHECK, new Expression[] {expression}, false, false);
+ else
+ return new CHRLiteral(expression.location, SpecialCHRRelation.EXECUTE, new Expression[] {expression}, false, false);
+ }
+
+ private static CHRLiteral convertConstraint(boolean remove, Expression expression) {
+ long location = expression.location;
+ Expression[] parameters;
+ FieldAssignment[] fields = null;
+ if(expression instanceof EApply) {
+ EApply apply = (EApply)expression;
+ parameters = apply.parameters;
+ expression = apply.function;
+ }
+ else if(expression instanceof ERecord) {
+ ERecord record = (ERecord)expression;
+ parameters = null;
+ fields = record.fields;
+ expression = record.constructor;
+ }
+ else // if(expression instanceof EVar)
+ parameters = Expression.EMPTY_ARRAY;
+ EVar var = (EVar)expression; // this should succeed because of isConstraint test
+ CHRLiteral literal = new CHRLiteral(location, new UnresolvedCHRRelation(var.location, var.name),
+ parameters, remove, false);
+ literal.fields = fields;
+ return literal;
+ }
+}