Skip to content

Commit 3c28d02

Browse files
author
qtran
committed
Merge pull request #260 from NumberFour/GH-315
GH-315: As a developer, I want to use @StringBased enum literals as keys for Object-literals
2 parents 034128e + 534b9c9 commit 3c28d02

File tree

18 files changed

+1447
-1201
lines changed

18 files changed

+1447
-1201
lines changed

plugins/eu.numberfour.n4js.ts.model/emf-gen/eu/numberfour/n4js/ts/types/TEnumLiteral.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,13 @@ public interface TEnumLiteral extends SyntaxRelatedTElement, IdentifiableElement
5555
*/
5656
void setValue(String value);
5757

58+
/**
59+
* <!-- begin-user-doc -->
60+
* <!-- end-user-doc -->
61+
* @model kind="operation" unique="false"
62+
* annotation="http://www.eclipse.org/emf/2002/GenModel body='<%java.lang.String%> _elvis = null;\n<%java.lang.String%> _value = this.getValue();\nif (_value != null)\n{\n\t_elvis = _value;\n} else\n{\n\t<%java.lang.String%> _name = this.getName();\n\t_elvis = _name;\n}\nreturn _elvis;'"
63+
* @generated
64+
*/
65+
String getValueOrName();
66+
5867
} // TEnumLiteral

plugins/eu.numberfour.n4js.ts.model/emf-gen/eu/numberfour/n4js/ts/types/TypesPackage.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10825,14 +10825,23 @@ public interface TypesPackage extends EPackage {
1082510825
*/
1082610826
int TENUM_LITERAL___GET_CONTAINING_MODULE = SYNTAX_RELATED_TELEMENT_OPERATION_COUNT + 0;
1082710827

10828+
/**
10829+
* The operation id for the '<em>Get Value Or Name</em>' operation.
10830+
* <!-- begin-user-doc -->
10831+
* <!-- end-user-doc -->
10832+
* @generated
10833+
* @ordered
10834+
*/
10835+
int TENUM_LITERAL___GET_VALUE_OR_NAME = SYNTAX_RELATED_TELEMENT_OPERATION_COUNT + 1;
10836+
1082810837
/**
1082910838
* The number of operations of the '<em>TEnum Literal</em>' class.
1083010839
* <!-- begin-user-doc -->
1083110840
* <!-- end-user-doc -->
1083210841
* @generated
1083310842
* @ordered
1083410843
*/
10835-
int TENUM_LITERAL_OPERATION_COUNT = SYNTAX_RELATED_TELEMENT_OPERATION_COUNT + 1;
10844+
int TENUM_LITERAL_OPERATION_COUNT = SYNTAX_RELATED_TELEMENT_OPERATION_COUNT + 2;
1083610845

1083710846
/**
1083810847
* The meta object id for the '{@link eu.numberfour.n4js.ts.types.impl.TVariableImpl <em>TVariable</em>}' class.
@@ -13540,6 +13549,16 @@ public interface TypesPackage extends EPackage {
1354013549
*/
1354113550
EAttribute getTEnumLiteral_Value();
1354213551

13552+
/**
13553+
* Returns the meta object for the '{@link eu.numberfour.n4js.ts.types.TEnumLiteral#getValueOrName() <em>Get Value Or Name</em>}' operation.
13554+
* <!-- begin-user-doc -->
13555+
* <!-- end-user-doc -->
13556+
* @return the meta object for the '<em>Get Value Or Name</em>' operation.
13557+
* @see eu.numberfour.n4js.ts.types.TEnumLiteral#getValueOrName()
13558+
* @generated
13559+
*/
13560+
EOperation getTEnumLiteral__GetValueOrName();
13561+
1354313562
/**
1354413563
* Returns the meta object for class '{@link eu.numberfour.n4js.ts.types.SyntaxRelatedTElement <em>Syntax Related TElement</em>}'.
1354513564
* <!-- begin-user-doc -->
@@ -15736,6 +15755,14 @@ interface Literals {
1573615755
*/
1573715756
EAttribute TENUM_LITERAL__VALUE = eINSTANCE.getTEnumLiteral_Value();
1573815757

15758+
/**
15759+
* The meta object literal for the '<em><b>Get Value Or Name</b></em>' operation.
15760+
* <!-- begin-user-doc -->
15761+
* <!-- end-user-doc -->
15762+
* @generated
15763+
*/
15764+
EOperation TENUM_LITERAL___GET_VALUE_OR_NAME = eINSTANCE.getTEnumLiteral__GetValueOrName();
15765+
1573915766
/**
1574015767
* The meta object literal for the '{@link eu.numberfour.n4js.ts.types.impl.SyntaxRelatedTElementImpl <em>Syntax Related TElement</em>}' class.
1574115768
* <!-- begin-user-doc -->

plugins/eu.numberfour.n4js.ts.model/emf-gen/eu/numberfour/n4js/ts/types/impl/TEnumLiteralImpl.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ public void setValue(String newValue) {
141141
eNotify(new ENotificationImpl(this, Notification.SET, TypesPackage.TENUM_LITERAL__VALUE, oldValue, value));
142142
}
143143

144+
/**
145+
* <!-- begin-user-doc -->
146+
* <!-- end-user-doc -->
147+
* @generated
148+
*/
149+
public String getValueOrName() {
150+
String _elvis = null;
151+
String _value = this.getValue();
152+
if (_value != null) {
153+
_elvis = _value;
154+
} else {
155+
String _name = this.getName();
156+
_elvis = _name;
157+
}
158+
return _elvis;
159+
}
160+
144161
/**
145162
* <!-- begin-user-doc -->
146163
* <!-- end-user-doc -->
@@ -289,6 +306,8 @@ public int eDerivedOperationID(int baseOperationID, Class<?> baseClass) {
289306
@Override
290307
public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException {
291308
switch (operationID) {
309+
case TypesPackage.TENUM_LITERAL___GET_VALUE_OR_NAME:
310+
return getValueOrName();
292311
case TypesPackage.TENUM_LITERAL___GET_CONTAINING_MODULE:
293312
return getContainingModule();
294313
}

plugins/eu.numberfour.n4js.ts.model/emf-gen/eu/numberfour/n4js/ts/types/impl/TypesPackageImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,6 +2714,15 @@ public EAttribute getTEnumLiteral_Value() {
27142714
return (EAttribute)tEnumLiteralEClass.getEStructuralFeatures().get(0);
27152715
}
27162716

2717+
/**
2718+
* <!-- begin-user-doc -->
2719+
* <!-- end-user-doc -->
2720+
* @generated
2721+
*/
2722+
public EOperation getTEnumLiteral__GetValueOrName() {
2723+
return tEnumLiteralEClass.getEOperations().get(0);
2724+
}
2725+
27172726
/**
27182727
* <!-- begin-user-doc -->
27192728
* <!-- end-user-doc -->
@@ -3187,6 +3196,7 @@ public void createPackageContents() {
31873196

31883197
tEnumLiteralEClass = createEClass(TENUM_LITERAL);
31893198
createEAttribute(tEnumLiteralEClass, TENUM_LITERAL__VALUE);
3199+
createEOperation(tEnumLiteralEClass, TENUM_LITERAL___GET_VALUE_OR_NAME);
31903200

31913201
syntaxRelatedTElementEClass = createEClass(SYNTAX_RELATED_TELEMENT);
31923202
createEReference(syntaxRelatedTElementEClass, SYNTAX_RELATED_TELEMENT__AST_ELEMENT);
@@ -3753,6 +3763,8 @@ public void initializePackageContents() {
37533763
initEClass(tEnumLiteralEClass, TEnumLiteral.class, "TEnumLiteral", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
37543764
initEAttribute(getTEnumLiteral_Value(), theEcorePackage.getEString(), "value", null, 0, 1, TEnumLiteral.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, !IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
37553765

3766+
initEOperation(getTEnumLiteral__GetValueOrName(), theEcorePackage.getEString(), "getValueOrName", 0, 1, !IS_UNIQUE, IS_ORDERED);
3767+
37563768
initEClass(syntaxRelatedTElementEClass, SyntaxRelatedTElement.class, "SyntaxRelatedTElement", IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
37573769
initEReference(getSyntaxRelatedTElement_AstElement(), theEcorePackage.getEObject(), null, "astElement", null, 0, 1, SyntaxRelatedTElement.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
37583770

plugins/eu.numberfour.n4js.ts.model/model/Types.xcore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,10 @@ class TEnum extends DeclaredTypeWithAccessModifier, SyntaxRelatedTElement {
14741474
*/
14751475
class TEnumLiteral extends SyntaxRelatedTElement, IdentifiableElement {
14761476
String value
1477+
1478+
op String getValueOrName() {
1479+
return value ?: name;
1480+
}
14771481
}
14781482

14791483
/*

plugins/eu.numberfour.n4js/src/eu/numberfour/n4js/resource/N4JSPreProcessor.xtend

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ package final class N4JSPreProcessor {
6565
if (target instanceof IdentifierRef) {
6666
if (target.idAsText == 'Symbol') {
6767
return ComputedPropertyNameValueConverter.SYMBOL_IDENTIFIER_PREFIX + expr.propertyAsText;
68+
} else {
69+
// this case implements support for literals of @StringBased enums
70+
// (but here we cannot ensure that 'target' is actually a @StringBased enum, because that would require
71+
// resolution of proxies which we are not allowed to do)
72+
return expr.propertyAsText;
6873
}
6974
}
7075
}

plugins/eu.numberfour.n4js/src/eu/numberfour/n4js/typesystem/n4js.xsemantics

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -615,13 +615,13 @@ from {
615615
G |- expr.target : var TypeRef targetTypeRef
616616
targetTypeRef = typeSystemHelper.resolveType(G, targetTypeRef);
617617

618-
val accessedBuiltInSymbol = G.getAccessedBuiltInSymbol(expr.index);
618+
val memberName = N4JSLanguageUtils.getMemberNameForIndexExpression(G, expr.index);
619619
val elementType = targetTypeRef.declaredType?.elementType
620620

621-
if (accessedBuiltInSymbol!==null) {
621+
if (memberName!==null && memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX)) {
622+
// the index (inside the []) is a symbol
622623
val declType = targetTypeRef.declaredType;
623624
if(declType instanceof ContainerType<?>) {
624-
val memberName = '#' + accessedBuiltInSymbol.name;
625625
val member = containerTypesHelper.fromContext(expr).findMember(declType,memberName,false,false); // TODO support for static access?
626626
if(member!==null) {
627627
G |- member : var TypeRef memberTypeRef
@@ -638,22 +638,11 @@ from {
638638
else {
639639
T = G.anyTypeRef
640640
}
641-
} else if (elementType !== null) {
642-
val declaredType = targetTypeRef.declaredType
643-
if (declaredType.generic && targetTypeRef.typeArgs.isEmpty) {
644-
T = G.anyTypeRef // later: evaluate name if possible, we may even want to return smth like intersect(allProperties)
645-
} else {
646-
val G2 = G.wrap
647-
typeSystemHelper.addSubstitutions(G2, targetTypeRef)
648-
G2.addThisType(targetTypeRef)
649-
G2 |- elementType ~> T
650-
}
651-
} else if (expr.index instanceof StringLiteral) {
641+
} else if (memberName!==null) {
652642
// indexing via constant computed-name, sub-cases: static or instance member, for the latter nominally-typed or structurally-typed receiver
653643
val staticAccess = (targetTypeRef instanceof TypeTypeRef)
654644
val checkVisibility = false // access modifiers checked in validation
655645
val scope = memberScopingHelper.createMemberScopeFor(targetTypeRef, expr, checkVisibility, staticAccess)
656-
val memberName = (expr.index as StringLiteral).value;
657646
val member = memberScopingHelper.findUniqueMemberForName(scope, memberName, staticAccess)
658647
if(member != null) {
659648
G |- member : var TypeRef memberTypeRef
@@ -664,6 +653,16 @@ from {
664653
} else {
665654
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
666655
}
656+
} else if (elementType !== null) {
657+
val declaredType = targetTypeRef.declaredType
658+
if (declaredType.generic && targetTypeRef.typeArgs.isEmpty) {
659+
T = G.anyTypeRef // later: evaluate name if possible, we may even want to return smth like intersect(allProperties)
660+
} else {
661+
val G2 = G.wrap
662+
typeSystemHelper.addSubstitutions(G2, targetTypeRef)
663+
G2.addThisType(targetTypeRef)
664+
G2 |- elementType ~> T
665+
}
667666
} else if (targetTypeRef.dynamic) {
668667
T = G.anyTypeRefDynamic
669668
} else {

plugins/eu.numberfour.n4js/src/eu/numberfour/n4js/utils/N4JSLanguageUtils.xtend

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import eu.numberfour.n4js.conversion.IdentifierValueConverter
1616
import eu.numberfour.n4js.n4JS.AbstractAnnotationList
1717
import eu.numberfour.n4js.n4JS.AnnotableElement
1818
import eu.numberfour.n4js.n4JS.ExportedVariableDeclaration
19+
import eu.numberfour.n4js.n4JS.Expression
1920
import eu.numberfour.n4js.n4JS.FormalParameter
2021
import eu.numberfour.n4js.n4JS.FunctionDeclaration
2122
import eu.numberfour.n4js.n4JS.FunctionDefinition
23+
import eu.numberfour.n4js.n4JS.IndexedAccessExpression
2224
import eu.numberfour.n4js.n4JS.N4ClassDeclaration
2325
import eu.numberfour.n4js.n4JS.N4ClassifierDeclaration
2426
import eu.numberfour.n4js.n4JS.N4EnumLiteral
@@ -29,10 +31,12 @@ import eu.numberfour.n4js.n4JS.N4MemberAnnotationList
2931
import eu.numberfour.n4js.n4JS.N4MemberDeclaration
3032
import eu.numberfour.n4js.n4JS.N4MethodDeclaration
3133
import eu.numberfour.n4js.n4JS.NumericLiteral
34+
import eu.numberfour.n4js.n4JS.ParameterizedPropertyAccessExpression
3235
import eu.numberfour.n4js.n4JS.PropertyAssignment
3336
import eu.numberfour.n4js.n4JS.PropertyAssignmentAnnotationList
3437
import eu.numberfour.n4js.n4JS.PropertyMethodDeclaration
3538
import eu.numberfour.n4js.n4JS.Script
39+
import eu.numberfour.n4js.n4JS.StringLiteral
3640
import eu.numberfour.n4js.n4JS.TypeDefiningElement
3741
import eu.numberfour.n4js.n4JS.UnaryExpression
3842
import eu.numberfour.n4js.n4JS.UnaryOperator
@@ -52,6 +56,7 @@ import eu.numberfour.n4js.ts.types.MemberAccessModifier
5256
import eu.numberfour.n4js.ts.types.TAnnotableElement
5357
import eu.numberfour.n4js.ts.types.TClass
5458
import eu.numberfour.n4js.ts.types.TClassifier
59+
import eu.numberfour.n4js.ts.types.TEnumLiteral
5560
import eu.numberfour.n4js.ts.types.TField
5661
import eu.numberfour.n4js.ts.types.TFunction
5762
import eu.numberfour.n4js.ts.types.TMember
@@ -599,4 +604,40 @@ class N4JSLanguageUtils {
599604
def static TypeRef getTypeVariableImplicitUpperBound(RuleEnvironment G) {
600605
return G.anyTypeRef;
601606
}
607+
608+
/**
609+
* Tells if the given expression is valid as an index within an {@link IndexedAccessExpression}.
610+
*/
611+
def static boolean isValidIndexExpression(RuleEnvironment G, Expression indexExpr) {
612+
if(indexExpr instanceof NumericLiteral || indexExpr instanceof StringLiteral) {
613+
return true;
614+
} else if(G.getAccessedBuiltInSymbol(indexExpr)!==null) {
615+
return true;
616+
} else if(indexExpr instanceof ParameterizedPropertyAccessExpression) {
617+
return indexExpr.property instanceof TEnumLiteral;
618+
}
619+
return false;
620+
}
621+
/**
622+
* If the given expression is a {@link #isValidIndexExpression(RuleEnvironment, Expression) valid index expression}
623+
* but is *not* numerical, then this method will return the name of the member the index access expression is
624+
* referring to. Returns <code>null</code> if the expression is invalid or numerical.
625+
*/
626+
def static String getMemberNameForIndexExpression(RuleEnvironment G, Expression indexExpr) {
627+
val accessedBuiltInSymbol = G.getAccessedBuiltInSymbol(indexExpr);
628+
if(accessedBuiltInSymbol!==null) {
629+
return SYMBOL_IDENTIFIER_PREFIX + accessedBuiltInSymbol.name;
630+
} else {
631+
return switch(indexExpr) {
632+
StringLiteral:
633+
indexExpr.value
634+
ParameterizedPropertyAccessExpression: {
635+
val prop = indexExpr.property;
636+
if(prop instanceof TEnumLiteral) {
637+
prop.valueOrName
638+
}
639+
}
640+
};
641+
}
642+
}
602643
}

0 commit comments

Comments
 (0)