support for in operation syntax, refactor, test, range sugar syntax

This commit is contained in:
Pavel Talanov
2011-12-13 17:44:06 +04:00
parent a105792a85
commit 936e3f0d84
11 changed files with 164 additions and 20 deletions
@@ -328,8 +328,11 @@ public class AstUtil {
return equals(expression, program.getTrueLiteral()); return equals(expression, program.getTrueLiteral());
} }
public static JsBinaryOperation sum(JsExpression left, JsExpression right) { public static JsBinaryOperation sum(JsExpression left, JsExpression right) {
return new JsBinaryOperation(JsBinaryOperator.ADD, left, right); return new JsBinaryOperation(JsBinaryOperator.ADD, left, right);
} }
public static JsBinaryOperation subtract(JsExpression left, JsExpression right) {
return new JsBinaryOperation(JsBinaryOperator.SUB, left, right);
}
} }
@@ -4,21 +4,18 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.types.JetStandardLibrary; import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.k2js.translate.intrinsic.array.ArrayGetIntrinsic; import org.jetbrains.k2js.translate.intrinsic.array.ArrayGetIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.array.ArrayNullConstructorIntrinsic; import org.jetbrains.k2js.translate.intrinsic.array.ArrayNullConstructorIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.array.ArraySetIntrinsic; import org.jetbrains.k2js.translate.intrinsic.array.ArraySetIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveBinaryOperationIntrinsic; import org.jetbrains.k2js.translate.intrinsic.primitive.*;
import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveCompareToIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveEqualsIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveUnaryOperationIntrinsic;
import org.jetbrains.k2js.translate.operation.OperatorTable; import org.jetbrains.k2js.translate.operation.OperatorTable;
import org.jetbrains.k2js.translate.utils.DescriptorUtils; import org.jetbrains.k2js.translate.utils.DescriptorUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getFunctionByName; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getFunctionByName;
/** /**
@@ -55,6 +52,7 @@ public final class Intrinsics {
private void declareOperatorIntrinsics() { private void declareOperatorIntrinsics() {
IntrinsicDeclarationVisitor visitor = new IntrinsicDeclarationVisitor(this); IntrinsicDeclarationVisitor visitor = new IntrinsicDeclarationVisitor(this);
for (DeclarationDescriptor descriptor : library.getLibraryScope().getAllDescriptors()) { for (DeclarationDescriptor descriptor : library.getLibraryScope().getAllDescriptors()) {
//noinspection NullableProblems
descriptor.accept(visitor, null); descriptor.accept(visitor, null);
} }
} }
@@ -75,25 +73,33 @@ public final class Intrinsics {
addEqualsIntrinsics(descriptor); addEqualsIntrinsics(descriptor);
addUnaryIntrinsics(descriptor); addUnaryIntrinsics(descriptor);
addBinaryIntrinsics(descriptor); addBinaryIntrinsics(descriptor);
addRangeToIntrinsics(descriptor);
}
private void addRangeToIntrinsics(@NotNull FunctionDescriptor descriptor) {
String functionName = descriptor.getName();
if (functionName.equals("rangeTo")) {
functionIntrinsics.put(descriptor, PrimitiveRangeToIntrinsic.newInstance());
}
} }
private void addCompareToIntrinsics(@NotNull FunctionDescriptor descriptor) { private void addCompareToIntrinsics(@NotNull FunctionDescriptor descriptor) {
String functionName = descriptor.getName(); String functionName = descriptor.getName();
if (functionName.equals("compareTo")) { if (functionName.equals(COMPARE_TO)) {
compareToIntrinsics.put(descriptor, PrimitiveCompareToIntrinsic.newInstance()); compareToIntrinsics.put(descriptor, PrimitiveCompareToIntrinsic.newInstance());
} }
} }
private void addEqualsIntrinsics(@NotNull FunctionDescriptor descriptor) { private void addEqualsIntrinsics(@NotNull FunctionDescriptor descriptor) {
String functionName = descriptor.getName(); String functionName = descriptor.getName();
if (functionName.equals("equals")) { if (functionName.equals(EQUALS)) {
equalsIntrinsics.put(descriptor, PrimitiveEqualsIntrinsic.newInstance()); equalsIntrinsics.put(descriptor, PrimitiveEqualsIntrinsic.newInstance());
} }
} }
private void addUnaryIntrinsics(@NotNull FunctionDescriptor descriptor) { private void addUnaryIntrinsics(@NotNull FunctionDescriptor descriptor) {
String functionName = descriptor.getName(); String functionName = descriptor.getName();
JetToken token = OperatorConventions.UNARY_OPERATION_NAMES.inverse().get(functionName); JetToken token = UNARY_OPERATION_NAMES.inverse().get(functionName);
if (token == null) return; if (token == null) return;
boolean isUnary = !DescriptorUtils.hasParameters(descriptor); boolean isUnary = !DescriptorUtils.hasParameters(descriptor);
if (!isUnary) return; if (!isUnary) return;
@@ -105,9 +111,9 @@ public final class Intrinsics {
String functionName = descriptor.getName(); String functionName = descriptor.getName();
boolean isUnary = !DescriptorUtils.hasParameters(descriptor); boolean isUnary = !DescriptorUtils.hasParameters(descriptor);
if (isUnary) return; if (isUnary) return;
JetToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(functionName); JetToken token = BINARY_OPERATION_NAMES.inverse().get(functionName);
if (token == null) return; if (token == null) return;
//TODO: implement range and contains intrinsic //TODO: implement contains intrinsic
if (!OperatorTable.hasCorrespondingBinaryOperator(token)) return; if (!OperatorTable.hasCorrespondingBinaryOperator(token)) return;
functionIntrinsics.put(descriptor, PrimitiveBinaryOperationIntrinsic.newInstance(token)); functionIntrinsics.put(descriptor, PrimitiveBinaryOperationIntrinsic.newInstance(token));
} }
@@ -20,6 +20,9 @@ public final class PrimitiveCompareToIntrinsic extends CompareToIntrinsic {
return new PrimitiveCompareToIntrinsic(); return new PrimitiveCompareToIntrinsic();
} }
private PrimitiveCompareToIntrinsic() {
}
@NotNull @NotNull
public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments, public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
@@ -18,6 +18,9 @@ public final class PrimitiveEqualsIntrinsic extends EqualsIntrinsic {
return new PrimitiveEqualsIntrinsic(); return new PrimitiveEqualsIntrinsic();
} }
private PrimitiveEqualsIntrinsic() {
}
@NotNull @NotNull
public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments, public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
@@ -0,0 +1,40 @@
package org.jetbrains.k2js.translate.intrinsic.primitive;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsNew;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic;
import java.util.Arrays;
import java.util.List;
/**
* @author Talanov Pavel
*/
public final class PrimitiveRangeToIntrinsic implements FunctionIntrinsic {
@NotNull
public static PrimitiveRangeToIntrinsic newInstance() {
return new PrimitiveRangeToIntrinsic();
}
private PrimitiveRangeToIntrinsic() {
}
@NotNull
@Override
public JsExpression apply(@NotNull JsExpression rangeStart, @NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) {
assert arguments.size() == 1 : "RangeTo must have one argument.";
JsExpression rangeEnd = arguments.get(0);
JsBinaryOperation rangeSize = AstUtil.subtract(rangeEnd, rangeStart);
//TODO: provide a way not to hard code this value
JsNew numberRangeConstructorInvocation
= new JsNew(AstUtil.newQualifiedNameRef("Kotlin.NumberRange"));
numberRangeConstructorInvocation.setArguments(Arrays.asList(rangeStart, rangeSize));
return numberRangeConstructorInvocation;
}
}
@@ -17,6 +17,8 @@ import org.jetbrains.k2js.translate.reference.CallTranslator;
import java.util.Arrays; import java.util.Arrays;
import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator;
import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isEquals; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isEquals;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken;
@@ -51,21 +53,27 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translate() { private JsExpression translate() {
if (AssignmentTranslator.isAssignmentOperator(expression)) { if (isAssignmentOperator(expression)) {
return AssignmentTranslator.translate(expression, context()); return AssignmentTranslator.translate(expression, context());
} }
if (operationDescriptor == null) { if (isNotOverloadable()) {
return translateAsUnOverloadableBinaryOperation(); return translateAsUnOverloadableBinaryOperation();
} }
if (CompareToTranslator.isCompareToCall(expression, context())) { if (isCompareToCall(expression, context())) {
return CompareToTranslator.translate(expression, context()); return CompareToTranslator.translate(expression, context());
} }
assert operationDescriptor != null :
"Overloadable operations must have not null descriptor";
if (isEquals(operationDescriptor)) { if (isEquals(operationDescriptor)) {
return translateAsEqualsCall(); return translateAsEqualsCall();
} }
return CallTranslator.translate(expression, context()); return CallTranslator.translate(expression, context());
} }
private boolean isNotOverloadable() {
return operationDescriptor == null;
}
@NotNull @NotNull
private JsExpression translateAsEqualsCall() { private JsExpression translateAsEqualsCall() {
assert operationDescriptor != null : "Equals operation must resolve to descriptor."; assert operationDescriptor != null : "Equals operation must resolve to descriptor.";
@@ -23,11 +23,13 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForRe
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForCallExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForCallExpression;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getVariableDescriptorForVariableAsFunction; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getVariableDescriptorForVariableAsFunction;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isConstructorDescriptor; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isConstructorDescriptor;
import static org.jetbrains.k2js.translate.utils.PsiUtils.isInOperation;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.*; import static org.jetbrains.k2js.translate.utils.TranslationUtils.*;
/** /**
* @author Talanov Pavel * @author Talanov Pavel
*/ */
//TODO: move translate() static method into builder (consider!)
//TODO: write tests on calling backing fields as functions //TODO: write tests on calling backing fields as functions
public final class CallTranslator extends AbstractTranslator { public final class CallTranslator extends AbstractTranslator {
@@ -38,14 +40,14 @@ public final class CallTranslator extends AbstractTranslator {
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), unaryExpression.getOperationReference()); (context.bindingContext(), unaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor; assert descriptor instanceof FunctionDescriptor;
return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); return translate(context, receiver, arguments, (FunctionDescriptor) descriptor);
} }
//TODO: method too long //TODO: method too long
public static JsExpression translate(@NotNull JetDotQualifiedExpression dotExpression, public static JsExpression translate(@NotNull JetDotQualifiedExpression dotExpression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
//TODO: look for duplication
JsExpression receiver = translateReceiver(context, dotExpression); JsExpression receiver = translateReceiver(context, dotExpression);
//TODO: util?
JetExpression selectorExpression = dotExpression.getSelectorExpression(); JetExpression selectorExpression = dotExpression.getSelectorExpression();
assert selectorExpression instanceof JetCallExpression; assert selectorExpression instanceof JetCallExpression;
JetCallExpression callExpression = (JetCallExpression) selectorExpression; JetCallExpression callExpression = (JetCallExpression) selectorExpression;
@@ -53,7 +55,7 @@ public final class CallTranslator extends AbstractTranslator {
translateArgumentList(context, callExpression.getValueArguments()); translateArgumentList(context, callExpression.getValueArguments());
FunctionDescriptor descriptor = FunctionDescriptor descriptor =
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression); getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
return (new CallTranslator(receiver, arguments, descriptor, context)).translate(); return translate(context, receiver, arguments, descriptor);
} }
public static JsExpression translate(@NotNull JetCallExpression callExpression, public static JsExpression translate(@NotNull JetCallExpression callExpression,
@@ -62,18 +64,39 @@ public final class CallTranslator extends AbstractTranslator {
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression); getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
JsExpression receiver = getImplicitReceiver(context, descriptor); JsExpression receiver = getImplicitReceiver(context, descriptor);
List<JsExpression> arguments = translateArgumentList(context, callExpression.getValueArguments()); List<JsExpression> arguments = translateArgumentList(context, callExpression.getValueArguments());
return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); return translate(context, receiver, arguments, (FunctionDescriptor) descriptor);
} }
//TODO: refactor
public static JsExpression translate(@NotNull JetBinaryExpression binaryExpression, public static JsExpression translate(@NotNull JetBinaryExpression binaryExpression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
JsExpression receiver = translateLeftExpression(context, binaryExpression); JsExpression receiver = translateLeftExpression(context, binaryExpression);
List<JsExpression> arguments = Arrays.asList(translateRightExpression(context, binaryExpression)); List<JsExpression> arguments = Arrays.asList(translateRightExpression(context, binaryExpression));
//TODO: use PSI util method to get operation reference
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), binaryExpression.getOperationReference()); (context.bindingContext(), binaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor; assert descriptor instanceof FunctionDescriptor;
return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
if (isInOperation(binaryExpression)) {
return translateWithReceiverAndArgumentSwapped(context, receiver, arguments, functionDescriptor);
}
return translate(context, receiver, arguments, functionDescriptor);
}
@NotNull
private static JsExpression translateWithReceiverAndArgumentSwapped(@NotNull TranslationContext context,
@NotNull JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull FunctionDescriptor functionDescriptor) {
assert arguments.size() == 1 : "Must have one argument.";
return translate(context, arguments.get(0), Arrays.asList(receiver), functionDescriptor);
}
@NotNull
private static JsExpression translate(@NotNull TranslationContext context,
@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull FunctionDescriptor functionDescriptor) {
return (new CallTranslator(receiver, arguments, functionDescriptor, context)).translate();
} }
@Nullable @Nullable
@@ -52,6 +52,10 @@ public final class PsiUtils {
return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
} }
public static boolean isInOperation(@NotNull JetBinaryExpression binaryExpression) {
return binaryExpression.getOperationToken() == JetTokens.IN_KEYWORD;
}
@NotNull @NotNull
/*package*/ static JetExpression getCallee(@NotNull JetCallExpression expression) { /*package*/ static JetExpression getCallee(@NotNull JetCallExpression expression) {
JetExpression calleeExpression = expression.getCalleeExpression(); JetExpression calleeExpression = expression.getCalleeExpression();
@@ -19,5 +19,15 @@ public final class RangeTest extends TranslationTest {
testFooBoxIsTrue("explicitRange.kt"); testFooBoxIsTrue("explicitRange.kt");
} }
@Test
public void rangeSugarSyntax() throws Exception {
testFooBoxIsTrue("rangeSugarSyntax.kt");
}
@Test
public void intInRange() throws Exception {
testFooBoxIsTrue("intInRange.kt");
}
} }
@@ -0,0 +1,13 @@
namespace foo
fun box() : Boolean {
if (1 in -2..1) return false;
if (1 in -10..0) return false;
if (!(1 in 0..2)) return false;
if (!(1 in 1..2)) return false;
if (!(1 in -2..5)) return false;
return true;
}
@@ -0,0 +1,31 @@
namespace foo
fun box() : Boolean {
var oneToFive = 1..5
if (oneToFive.contains(5)) return false;
if (oneToFive.contains(0)) return false;
if (oneToFive.contains(-100)) return false;
if (oneToFive.contains(10)) return false;
if (!oneToFive.contains(1)) return false;
if (!oneToFive.contains(2)) return false;
if (!oneToFive.contains(3)) return false;
if (!oneToFive.contains(4)) return false;
if (!(oneToFive.start == 1)) return false;
if (!(oneToFive.size == 4)) return false;
if (!(oneToFive.end == 5)) return false;
var sum = 0;
for (i in oneToFive) {
sum += i;
}
for (i in oneToFive) {
System.out?.print(i)
}
if (sum != 10) return false;
return true;
}