package edu.ucsd.arcum.interpreter.ast.expressions; import static edu.ucsd.arcum.interpreter.parser.ArcumStructureParser.parseEmbeddedExpressionBody; import static edu.ucsd.arcum.interpreter.parser.BacktrackingScanner.TokenNameARCUMBEGINQUOTE; import static edu.ucsd.arcum.interpreter.parser.BacktrackingScanner.TokenNameARCUMVARIABLE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEOF; import java.util.List; import java.util.Set; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import edu.ucsd.arcum.exceptions.ArcumError; import edu.ucsd.arcum.exceptions.SourceLocation; import edu.ucsd.arcum.interpreter.ast.FormalParameter; import edu.ucsd.arcum.interpreter.ast.TraitSignature; import edu.ucsd.arcum.interpreter.parser.ArcumStructureParser; import edu.ucsd.arcum.interpreter.parser.BacktrackingScanner; import edu.ucsd.arcum.interpreter.parser.ArcumStructureParser.EmbeddedExpression; // A predicate-like statement, but uses Java code patterns instead public class PatternExpression extends ConstraintExpression { private final String pattern; private final ArcumStructureParser parser; private final List<EmbeddedExpression> embeddedExpressions; private final Set<String> references; private boolean isImmediate; public PatternExpression(SourceLocation location, String pattern, ArcumStructureParser parser, List<EmbeddedExpression> embeddedExpressions) { super(location); this.pattern = pattern; this.parser = parser; this.embeddedExpressions = Lists.newArrayList(embeddedExpressions); this.references = Sets.newHashSet(); this.isImmediate = false; scanPattern(); } private void scanPattern() { BacktrackingScanner scanner = new BacktrackingScanner(pattern, parser .getResource()); for (;;) { if (scanner.lookahead() == TokenNameEOF) { break; } else if (scanner.lookahead() == TokenNameARCUMVARIABLE) { references.add(scanner.getCurrentTokenString()); scanner.match(); } else if (scanner.lookaheadEquals(TokenNameARCUMBEGINQUOTE)) { EmbeddedExpression embed = parseEmbeddedExpressionBody(parser, scanner); ConstraintExpression expr = embed.getConstraintExpression(); Set<String> set = Sets.newHashSet(expr.getArcumVariableReferences()); set.remove(embed.getBoundVar().getIdentifier()); references.addAll(set); } else { scanner.match(); } } } public boolean hasEmbeddedExpressions() { return embeddedExpressions.size() > 0; } public List<EmbeddedExpression> getEmbeddedExpressions() { return embeddedExpressions; } public EmbeddedExpression getEmbeddedExpression(int index) { return embeddedExpressions.get(index); } @Override public String toString() { String fmt = (isImmediate) ? "<%s>" : "[%s]"; return String.format(fmt, pattern); } public String getPattern() { return pattern; } // scans the pattern string for Arcum variable references (those variables // in patterns that start with a '`' tick) and returns a set of them @Override public Set<String> getArcumVariableReferences() { return references; } @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != PatternExpression.class) { return false; } else { PatternExpression that = (PatternExpression)obj; return this.pattern.equals(that.pattern); } } @Override protected void doCheckUserDefinedPredicates(List<TraitSignature> tupleSets, Set<String> varsInScope) { for (EmbeddedExpression embeddedExpression : embeddedExpressions) { ConstraintExpression expr = embeddedExpression.getConstraintExpression(); Set<String> nextScope = Sets.newHashSet(varsInScope); nextScope.add(embeddedExpression.getBoundVar().getIdentifier()); expr.doCheckUserDefinedPredicates(tupleSets, nextScope); } for (String reference : references) { if (!varsInScope.contains(reference)) { ArcumError.fatalUserError(getPosition(), "Reference to undefined variable %s (check spelling or scope)", reference); } } } public boolean isImmediatePattern() { return isImmediate; } public void setImmediate(boolean isImmediate) { this.isImmediate = isImmediate; } @Override public Set<String> findAllTraitDependencies() { Set<String> result = Sets.newHashSet(); for (EmbeddedExpression embeddedExpression : embeddedExpressions) { ConstraintExpression expr = embeddedExpression.getConstraintExpression(); FormalParameter boundVar = embeddedExpression.getBoundVar(); Set<String> dependencies = expr.findAllTraitDependencies(); dependencies.remove(boundVar.getIdentifier()); result.addAll(dependencies); } result.addAll(references);// TASK return result; } // Because Java patterns can contain quoted Arcum code, which may compose lists // of expressions, they will need to be fully evaluated first @Override public Set<String> findNonMonotonicDependencies() { return this.findAllTraitDependencies(); } }