Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » boolean assignment operator not working as expected(XText boolean assignment operator)
boolean assignment operator not working as expected [message #1808611] Thu, 27 June 2019 19:54 Go to next message
Steve Hickman is currently offline Steve HickmanFriend
Messages: 56
Registered: August 2017
Member
LogicalRealRangeConstraint returns logical::RealRangeConstraint /*hidden (WS)*/:
    'rrc' name=ID (description=STRING)?    ('('|lowerBoundInclusive?='[') lowerBound=REAL? ':' upperBound=REAL? (')'|upperBoundInclusive?=']')
     ;


The above rule is one of many in my XText DSL. The syntax
('('|lowerBoundInclusive?='[') 
is meant to say that if a user specifies '[', then lowerBoundInclusive will be set to true, but if the users specifies '(' then lowerBoundInclusive will be set to false. The syntax
 (')'|upperBoundInclusive?=']')
should do a similar thing for upperBoundInclusive.

This does not appear to work as desired. The syntax does limit the choices for lowerBoundInclusive to either '[' or '(' but lowerBoundInclusive is set to true regardless of which one is used. Similarly for upperBoundInclusive.


What is wrong here?
Re: boolean assignment operator not working as expected [message #1808612 is a reply to message #1808611] Thu, 27 June 2019 20:01 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
hi, how do you test that? the following gives false to me:

class MyDslParsingTest {
	@Inject
	ParseHelper<LogicalRealRangeConstraint> parseHelper
	
	@Test
	def void loadModel() {
		val result = parseHelper.parse('''
			rrc x "y" (1:1)
		''')
		Assertions.assertNotNull(result)
		val errors = result.eResource.errors
		Assertions.assertTrue(errors.isEmpty, '''Unexpected errors: «errors.join(", ")»''')
		println(result.lowerBoundInclusive)
		println(result.upperBoundInclusive)
	}
}


do you have a custom manual metamodel with different default values?


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: boolean assignment operator not working as expected [message #1808613 is a reply to message #1808612] Thu, 27 June 2019 20:06 Go to previous messageGo to next message
Steve Hickman is currently offline Steve HickmanFriend
Messages: 56
Registered: August 2017
Member
There is a custom metamodel that I can't change (it is supplied to me). The defaults in it are true:

/**
 */
package face.v30.face.datamodel.logical.impl;

import face.v30.face.datamodel.logical.LogicalPackage;
import face.v30.face.datamodel.logical.RealRangeConstraint;

import java.lang.Boolean;

import org.eclipse.emf.common.notify.Notification;

import org.eclipse.emf.ecore.EClass;

import org.eclipse.emf.ecore.impl.ENotificationImpl;

/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>Real Range Constraint</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * </p>
 * <ul>
 *   <li>{@link face.v30.face.datamodel.logical.impl.RealRangeConstraintImpl#getLowerBound <em>Lower Bound</em>}</li>
 *   <li>{@link face.v30.face.datamodel.logical.impl.RealRangeConstraintImpl#getUpperBound <em>Upper Bound</em>}</li>
 *   <li>{@link face.v30.face.datamodel.logical.impl.RealRangeConstraintImpl#isLowerBoundInclusive <em>Lower Bound Inclusive</em>}</li>
 *   <li>{@link face.v30.face.datamodel.logical.impl.RealRangeConstraintImpl#isUpperBoundInclusive <em>Upper Bound Inclusive</em>}</li>
 * </ul>
 *
 * @generated
 */
public class RealRangeConstraintImpl extends RealConstraintImpl implements RealRangeConstraint {
	/**
	 * The default value of the '{@link #getLowerBound() <em>Lower Bound</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getLowerBound()
	 * @generated
	 * @ordered
	 */
	protected static final float LOWER_BOUND_EDEFAULT = 0.0F;

	/**
	 * The cached value of the '{@link #getLowerBound() <em>Lower Bound</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getLowerBound()
	 * @generated
	 * @ordered
	 */
	protected float lowerBound = LOWER_BOUND_EDEFAULT;

	/**
	 * The default value of the '{@link #getUpperBound() <em>Upper Bound</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getUpperBound()
	 * @generated
	 * @ordered
	 */
	protected static final float UPPER_BOUND_EDEFAULT = 0.0F;

	/**
	 * The cached value of the '{@link #getUpperBound() <em>Upper Bound</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getUpperBound()
	 * @generated
	 * @ordered
	 */
	protected float upperBound = UPPER_BOUND_EDEFAULT;

	/**
	 * The default value of the '{@link #isLowerBoundInclusive() <em>Lower Bound Inclusive</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isLowerBoundInclusive()
	 * @generated
	 * @ordered
	 */
	protected static final boolean LOWER_BOUND_INCLUSIVE_EDEFAULT = true;

	/**
	 * The cached value of the '{@link #isLowerBoundInclusive() <em>Lower Bound Inclusive</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isLowerBoundInclusive()
	 * @generated
	 * @ordered
	 */
	protected boolean lowerBoundInclusive = LOWER_BOUND_INCLUSIVE_EDEFAULT;

	/**
	 * The default value of the '{@link #isUpperBoundInclusive() <em>Upper Bound Inclusive</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isUpperBoundInclusive()
	 * @generated
	 * @ordered
	 */
	protected static final boolean UPPER_BOUND_INCLUSIVE_EDEFAULT = true;

	/**
	 * The cached value of the '{@link #isUpperBoundInclusive() <em>Upper Bound Inclusive</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #isUpperBoundInclusive()
	 * @generated
	 * @ordered
	 */
	protected boolean upperBoundInclusive = UPPER_BOUND_INCLUSIVE_EDEFAULT;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected RealRangeConstraintImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return LogicalPackage.Literals.REAL_RANGE_CONSTRAINT;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public float getLowerBound() {
		return lowerBound;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void setLowerBound(float newLowerBound) {
		float oldLowerBound = lowerBound;
		lowerBound = newLowerBound;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND, oldLowerBound, lowerBound));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public float getUpperBound() {
		return upperBound;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void setUpperBound(float newUpperBound) {
		float oldUpperBound = upperBound;
		upperBound = newUpperBound;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND, oldUpperBound, upperBound));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean isLowerBoundInclusive() {
		return lowerBoundInclusive;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void setLowerBoundInclusive(boolean newLowerBoundInclusive) {
		boolean oldLowerBoundInclusive = lowerBoundInclusive;
		lowerBoundInclusive = newLowerBoundInclusive;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND_INCLUSIVE, oldLowerBoundInclusive, lowerBoundInclusive));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean isUpperBoundInclusive() {
		return upperBoundInclusive;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void setUpperBoundInclusive(boolean newUpperBoundInclusive) {
		boolean oldUpperBoundInclusive = upperBoundInclusive;
		upperBoundInclusive = newUpperBoundInclusive;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND_INCLUSIVE, oldUpperBoundInclusive, upperBoundInclusive));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND:
				return getLowerBound();
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND:
				return getUpperBound();
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND_INCLUSIVE:
				return isLowerBoundInclusive();
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND_INCLUSIVE:
				return isUpperBoundInclusive();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND:
				setLowerBound((Float)newValue);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND:
				setUpperBound((Float)newValue);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND_INCLUSIVE:
				setLowerBoundInclusive((Boolean)newValue);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND_INCLUSIVE:
				setUpperBoundInclusive((Boolean)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND:
				setLowerBound(LOWER_BOUND_EDEFAULT);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND:
				setUpperBound(UPPER_BOUND_EDEFAULT);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND_INCLUSIVE:
				setLowerBoundInclusive(LOWER_BOUND_INCLUSIVE_EDEFAULT);
				return;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND_INCLUSIVE:
				setUpperBoundInclusive(UPPER_BOUND_INCLUSIVE_EDEFAULT);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND:
				return lowerBound != LOWER_BOUND_EDEFAULT;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND:
				return upperBound != UPPER_BOUND_EDEFAULT;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__LOWER_BOUND_INCLUSIVE:
				return lowerBoundInclusive != LOWER_BOUND_INCLUSIVE_EDEFAULT;
			case LogicalPackage.REAL_RANGE_CONSTRAINT__UPPER_BOUND_INCLUSIVE:
				return upperBoundInclusive != UPPER_BOUND_INCLUSIVE_EDEFAULT;
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuilder result = new StringBuilder(super.toString());
		result.append(" (lowerBound: ");
		result.append(lowerBound);
		result.append(", upperBound: ");
		result.append(upperBound);
		result.append(", lowerBoundInclusive: ");
		result.append(lowerBoundInclusive);
		result.append(", upperBoundInclusive: ");
		result.append(upperBoundInclusive);
		result.append(')');
		return result.toString();
	}

} //RealRangeConstraintImpl

Re: boolean assignment operator not working as expected [message #1808614 is a reply to message #1808613] Thu, 27 June 2019 20:09 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
maybe you can use a custom DefaultEcoreElementsFactory
and do something like

public class MyDslDefaultEcoreElementFactory extends DefaultEcoreElementFactory {

@Override
public EObject create(EClassifier classifier) {
EObject created = super.create(classifier);
if (created instanceof LogicalRealRangeConstraint) {
((LogicalRealRangeConstraint)created).setLowerBoundInclusive(false);
((LogicalRealRangeConstraint)created).setUpperBoundInclusive(false);
}
return created;
}

}

if this has bad side effects (e.g. on serialization needs to be tested, maybe there customization is needed too)


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: boolean assignment operator not working as expected [message #1808615 is a reply to message #1808613] Thu, 27 June 2019 20:09 Go to previous messageGo to next message
Steve Hickman is currently offline Steve HickmanFriend
Messages: 56
Registered: August 2017
Member
Is there some way to get the equivalent of ?= that will set a value to false if it is consumed?
Re: boolean assignment operator not working as expected [message #1808616 is a reply to message #1808615] Thu, 27 June 2019 20:10 Go to previous messageGo to next message
Steve Hickman is currently offline Steve HickmanFriend
Messages: 56
Registered: August 2017
Member
Thanks for your rapid response Christian. I'll add the factory change you suggest.
Re: boolean assignment operator not working as expected [message #1808617 is a reply to message #1808616] Thu, 27 June 2019 20:18 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
i am not aware of a feature for that in the grammar


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: boolean assignment operator not working as expected [message #1808619 is a reply to message #1808617] Thu, 27 June 2019 20:28 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
maybe you can use a different grammar pattern + a value converter

generate myDsl "http://www.xtext.org/example/mydsl1/MyDsl"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore

LogicalRealRangeConstraint /*hidden (WS)*/:
	'rrc' name=ID (description=STRING)? lowerBoundInclusive=LOWER_BOUND_INCLUSIVE 
	lowerBound=INT? ':' upperBound=INT?
	upperBoundInclusive=UPPER_BOUND_INCLUSIVE;

LOWER_BOUND_INCLUSIVE returns ecore::EBoolean:
	"(" | "[";

UPPER_BOUND_INCLUSIVE returns ecore::EBoolean:
	")" | "]";


package org.xtext.example.mydsl1;

import org.eclipse.xtext.common.services.DefaultTerminalConverters;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.nodemodel.INode;

public class MyDslValueConverters extends DefaultTerminalConverters {
	
	@ValueConverter(rule = "LOWER_BOUND_INCLUSIVE")
	public IValueConverter<Boolean> LOWER_BOUND_INCLUSIVE() {
		return new IValueConverter<Boolean>() {

			@Override
			public Boolean toValue(String string, INode node) throws ValueConverterException {
				return "[".equals(string);
			}

			@Override
			public String toString(Boolean value) throws ValueConverterException {
				if (Boolean.TRUE.equals(value)) {
					return "[";
				}
				return "(";
			}
			
		};
	}
	
	
	@ValueConverter(rule = "UPPER_BOUND_INCLUSIVE")
	public IValueConverter<Boolean> UPPER_BOUND_INCLUSIVE() {
		return new IValueConverter<Boolean>() {

			@Override
			public Boolean toValue(String string, INode node) throws ValueConverterException {
				return "]".equals(string);
			}

			@Override
			public String toString(Boolean value) throws ValueConverterException {
				if (Boolean.TRUE.equals(value)) {
					return "]";
				}
				return ")";
			}
			
		};
	}

}



Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: boolean assignment operator not working as expected [message #1808621 is a reply to message #1808619] Thu, 27 June 2019 20:58 Go to previous messageGo to next message
Steve Hickman is currently offline Steve HickmanFriend
Messages: 56
Registered: August 2017
Member
The first approach (the derived DefaultEcoreElementFactory) works and does not appear to cause problems with serialization. I haven't had time to try the second approach yet.

Thanks again!
Re: boolean assignment operator not working as expected [message #1808632 is a reply to message #1808621] Fri, 28 June 2019 05:25 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 7655
Registered: July 2009
Senior Member
Hi

I've hit this problem too when I've tried to use a default-true Boolean literal with Xtext. I've had the flexibility to change the metamodel / model reader to workaround the problem. Your less flexible case makes clear that this is arguably an Xtext bug....

An unsettable EMF Boolean has three values internally, true, false, unset. The unset value is only detectable by using eIsSet().

Once a default is supplied, the external view of the Boolean is explicitly-true, explicitly-false, implicitly-default.

The Xtext grammar supports only two values. explicitly-true, implicitly-default.

With a false default, the Xtext grammar gives explicitly-true, implicitly-false which is useable.

With a true default, the Xtext grammar gives explicitly-true, implicitly-true which is not useable.

Therefore the Xtext parser should replace true assignments to Booleans that are default true, with false assignments on all alternative control paths. Or Xtext should support three values ?= for explicitly true, ~= for explicitly false, nothing for default.

(The problem just gets worse if the EMF Boolean is not-unsettable removing the internally unset value).

Regards

Ed Willink
Re: boolean assignment operator not working as expected [message #1808633 is a reply to message #1808632] Fri, 28 June 2019 05:27 Go to previous message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
@Ed issue + PR welcome

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Previous Topic:Reorder a model via formatter2 or validation and quick fix
Next Topic:Cross referenced element name is getting replaced
Goto Forum:
  


Current Time: Tue Apr 16 21:00:45 GMT 2024

Powered by FUDForum. Page generated in 3.68694 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top