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

org.eclipse.epsilon.ecl.dom.MatchRule Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2008-2023 The University of York.
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 * 
 * Contributors:
 *     Dimitrios Kolovos - initial API and implementation
 *     Antonio Garcia-Dominguez - protect against NPE in toString()
 ******************************************************************************/
package org.eclipse.epsilon.ecl.dom;

import java.util.Collection;

import org.eclipse.epsilon.common.module.IModule;
import org.eclipse.epsilon.common.parse.AST;
import org.eclipse.epsilon.common.util.AstUtil;
import org.eclipse.epsilon.ecl.exceptions.EclNotApplicableSuperRuleException;
import org.eclipse.epsilon.ecl.execute.context.IEclContext;
import org.eclipse.epsilon.ecl.parse.EclParser;
import org.eclipse.epsilon.ecl.trace.Match;
import org.eclipse.epsilon.ecl.trace.MatchTrace;
import org.eclipse.epsilon.eol.dom.ExecutableBlock;
import org.eclipse.epsilon.eol.dom.Parameter;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.execute.context.FrameStack;
import org.eclipse.epsilon.eol.execute.context.FrameType;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.types.EolMap;
import org.eclipse.epsilon.erl.dom.ExtensibleNamedRule;

public class MatchRule extends ExtensibleNamedRule {
	
	protected ExecutableBlock compareBlock;
	protected ExecutableBlock guardBlock;
	protected ExecutableBlock doBlock;
	protected ExecutableBlock> leftDomainBlock;
	protected ExecutableBlock> rightDomainBlock;
	protected Parameter leftParameter, rightParameter;

	@Override
	public AST getSuperRulesAst(AST cst) {
		return AstUtil.getChild(cst, EclParser.EXTENDS);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void build(AST cst, IModule module) {
		super.build(cst, module);
		this.leftDomainBlock = (ExecutableBlock>) module.createAst(AstUtil.getChild(cst, EclParser.LEFTDOMAIN), this);
		this.rightDomainBlock = (ExecutableBlock>) module.createAst(AstUtil.getChild(cst, EclParser.RIGHTDOMAIN), this);
		leftParameter = (Parameter) module.createAst(cst.getSecondChild(), this);
		if(leftDomainBlock == null) {
			rightParameter = (Parameter) module.createAst(cst.getThirdChild(), this);
		}
		else {
			rightParameter = (Parameter) module.createAst(cst.getFourthChild(), this);
		}
		this.compareBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EclParser.COMPARE), this);
		this.doBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EclParser.DO), this);
		this.guardBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EclParser.GUARD), this);
	}
	
	public boolean appliesTo(Object left, Object right, IEclContext context, boolean ofTypeOnly) throws EolRuntimeException {
		final boolean guardSatisfied, oto = !(!ofTypeOnly || isGreedy(context));
		final boolean appliesToTypes = getAllInstances(leftParameter, context, oto).contains(left) &&
			getAllInstances(rightParameter, context, oto).contains(right);
		
		if (!isAbstract(context) && appliesToTypes && guardBlock != null) {
			guardSatisfied = guardBlock.execute(context, 
				Variable.createReadOnlyVariable(leftParameter.getName(), left),
				Variable.createReadOnlyVariable(rightParameter.getName(), right),
				Variable.createReadOnlyVariable("self", this)
			);
		}
		else guardSatisfied = true;
		
		return appliesToTypes && guardSatisfied;
	}
	
	public Collection getLeftInstances(IEclContext context, boolean ofTypeOnly) throws EolRuntimeException {
		if (leftDomainBlock == null) {
			return getAllInstances(leftParameter, context, ofTypeOnly);
		}
		else {
			return leftDomainBlock.execute(context, true);
		}
	}
	
	public Collection getRightInstances(IEclContext context, boolean ofTypeOnly) throws EolRuntimeException {
		if (rightDomainBlock == null) {
			return getAllInstances(rightParameter, context, ofTypeOnly);
		}
		else { 
			return rightDomainBlock.execute(context, true);
		}
	}
	
	public Collection getRightInstances(IEclContext context, boolean ofTypeOnly, Object left) throws EolRuntimeException {
		if (rightDomainBlock == null) {
			return getAllInstances(rightParameter, context, ofTypeOnly);
		}
		else {
			if(rightDomainBlock.getText().equals("from")) {
				FrameStack scope = context.getFrameStack();
				scope.enterLocal(FrameType.PROTECTED, this);
				scope.put(Variable.createReadOnlyVariable(leftParameter.getName(), left));
			}
			return rightDomainBlock.execute(context, true);
		}
	}
	
	public Match matchPair(IEclContext context, boolean ofTypeOnly, Object leftInstance, Object rightInstance) throws EolRuntimeException {
		if (!ofTypeOnly && context.getMatchTrace().getMatch(leftInstance, rightInstance) != null) {
			return null;
		}
		
		// Try to find a rule with ofTypeOnly = true
		if (appliesTo(leftInstance, rightInstance, context, true)) {
			return match(leftInstance, rightInstance, context, null, false);
		}
		//TODO: Why both branches do same thing?
		// Else find a rule with onlyOfClass = false
		else if (appliesTo(leftInstance, rightInstance, context, false)) {
			return match(leftInstance, rightInstance, context, null, false);
		}
		return null;
	}
	
	/**
	 * Matches left against right
	 * @param left The left object
	 * @param right The right object
	 * @param context The context in which the ECL program is running
	 * @param asSuperRule Shows if the rule is executed as a super-rule of another rule
	 * @param forcedMatch Whether to add the match to the trace (?)
	 * @return
	 * @throws EolRuntimeException
	 */
	//TOOD: Variables defined in the guard should be accessible in the compare and do parts
	public Match match(Object left, Object right, IEclContext context, EolMap matchInfo, boolean forcedMatch) throws EolRuntimeException {
		MatchTrace matchTrace = context.getMatchTrace(), tempMatchTrace = context.getTempMatchTrace();
		Match match = null, tempMatch = null;
		boolean asSuperRule = matchInfo != null;
		 
		if (!asSuperRule) {
			// See if there is a match for left<->right in the temp match trace
			// If there is no match in the temp trace, create one
			// and add it to the temp trace
			// Else, return the temp match
			if ((tempMatch = tempMatchTrace.getMatch(left, right)) == null) {
				tempMatchTrace.add(tempMatch = new Match(left, right, true, this));
			}
			else {
				return tempMatch;
			}
			
			// See if left<->right have been already matched
			if ((match = matchTrace.getMatch(left, right)) != null) {
				tempMatchTrace.remove(tempMatch);
				return match;
			}
		}
		else if (!appliesTo(left, right, context, false)) {
			throw new EclNotApplicableSuperRuleException(left, right, this, context);
		}
		// If they have not been matched, construct their Match
		match = new Match(left, right, true, this);
		
		// Execute all the super-rules
		if (!superRules.isEmpty()) {
			boolean matching = true;
			for (ExtensibleNamedRule rule : superRules) {
				MatchRule matchRule = (MatchRule) rule;
				Match superRuleMatch = matchRule.match(left, right, context, match.getInfo(), false);
				matching = matching && superRuleMatch.isMatching();
			}
			match.setMatching(matching);
			
			if (!matching) {
				tempMatchTrace.remove(tempMatch);
				matchTrace.add(match);
				return match;
			}
		}

		FrameStack scope = context.getFrameStack();
		scope.enterLocal(FrameType.PROTECTED, this);
		
		// Execute the compare part of the rule
		
		EolMap info = asSuperRule ? matchInfo : match.getInfo();
		
		scope.put(
			Variable.createReadOnlyVariable(leftParameter.getName(), left),
			Variable.createReadOnlyVariable(rightParameter.getName(), right),
			Variable.createReadOnlyVariable("matchInfo", info),
			Variable.createReadOnlyVariable("self", this)
		);
		
		if (compareBlock != null) {
			match.setMatching(compareBlock.execute(context, false));
		}
		else if (superRules.isEmpty()) {
			match.setMatching(false);
		}
		
		if (!asSuperRule) {
			// Before exiting remove the temp match
			// we created from the temp match trace
			tempMatchTrace.remove(tempMatch);
			
			// If there are temp matches, it means
			// that the matching decision is based
			// upon rules that may not eventually hold
			// Therefore, in that case we should not
			// add a permanent match
			
			//TODO : See if this affects cyclic dependencies
			if (forcedMatch || tempMatchTrace.isEmpty()) {
				matchTrace.add(match);
			}
		}
		
		// Execute the do part of the rule
		if (doBlock != null && match.isMatching()) {
			doBlock.execute(context, false);
		}
		
		scope.leaveLocal(this);

		return match;
	}

	@Override
	public String toString() {
		return String.format("%s (%s, %s)", getName(),
			leftParameter == null ? "(no left)" : leftParameter.getTypeName(),
			rightParameter == null ? "(no right)" : rightParameter.getTypeName());
	}
	
	public boolean isRightDomainDynamic() {
		return rightDomainBlock!=null && rightDomainBlock.getText().equals("from");
	}
	
	public ExecutableBlock getCompareBlock(){
		return compareBlock;
	}
	
	public ExecutableBlock> getLeftDomainBlock(){
		return leftDomainBlock;
	}
	
	public ExecutableBlock> getRightDomainBlock(){
		return rightDomainBlock;
	}
	
	public Parameter getLeftParameter(){
		return leftParameter;
	}
	
	public Parameter getRightParameter(){
		return rightParameter;
	}
	
	public ExecutableBlock getGuardBlock() {
		return guardBlock;
	}
	
	public ExecutableBlock getDoBlock() {
		return doBlock;
	}
	
	public void accept(IEclVisitor visitor) {
		visitor.visit(this);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy