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());
}
public static JsBinaryOperation sum(JsExpression left, JsExpression 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.FunctionDescriptor;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.k2js.translate.intrinsic.array.ArrayGetIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.array.ArrayNullConstructorIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.array.ArraySetIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveBinaryOperationIntrinsic;
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.intrinsic.primitive.*;
import org.jetbrains.k2js.translate.operation.OperatorTable;
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
import java.util.HashMap;
import java.util.Map;
import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getFunctionByName;
/**
@@ -55,6 +52,7 @@ public final class Intrinsics {
private void declareOperatorIntrinsics() {
IntrinsicDeclarationVisitor visitor = new IntrinsicDeclarationVisitor(this);
for (DeclarationDescriptor descriptor : library.getLibraryScope().getAllDescriptors()) {
//noinspection NullableProblems
descriptor.accept(visitor, null);
}
}
@@ -75,25 +73,33 @@ public final class Intrinsics {
addEqualsIntrinsics(descriptor);
addUnaryIntrinsics(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) {
String functionName = descriptor.getName();
if (functionName.equals("compareTo")) {
if (functionName.equals(COMPARE_TO)) {
compareToIntrinsics.put(descriptor, PrimitiveCompareToIntrinsic.newInstance());
}
}
private void addEqualsIntrinsics(@NotNull FunctionDescriptor descriptor) {
String functionName = descriptor.getName();
if (functionName.equals("equals")) {
if (functionName.equals(EQUALS)) {
equalsIntrinsics.put(descriptor, PrimitiveEqualsIntrinsic.newInstance());
}
}
private void addUnaryIntrinsics(@NotNull FunctionDescriptor descriptor) {
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;
boolean isUnary = !DescriptorUtils.hasParameters(descriptor);
if (!isUnary) return;
@@ -105,9 +111,9 @@ public final class Intrinsics {
String functionName = descriptor.getName();
boolean isUnary = !DescriptorUtils.hasParameters(descriptor);
if (isUnary) return;
JetToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(functionName);
JetToken token = BINARY_OPERATION_NAMES.inverse().get(functionName);
if (token == null) return;
//TODO: implement range and contains intrinsic
//TODO: implement contains intrinsic
if (!OperatorTable.hasCorrespondingBinaryOperator(token)) return;
functionIntrinsics.put(descriptor, PrimitiveBinaryOperationIntrinsic.newInstance(token));
}
@@ -20,6 +20,9 @@ public final class PrimitiveCompareToIntrinsic extends CompareToIntrinsic {
return new PrimitiveCompareToIntrinsic();
}
private PrimitiveCompareToIntrinsic() {
}
@NotNull
public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) {
@@ -18,6 +18,9 @@ public final class PrimitiveEqualsIntrinsic extends EqualsIntrinsic {
return new PrimitiveEqualsIntrinsic();
}
private PrimitiveEqualsIntrinsic() {
}
@NotNull
public JsExpression apply(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments,
@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 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.DescriptorUtils.isEquals;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken;
@@ -51,21 +53,27 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression translate() {
if (AssignmentTranslator.isAssignmentOperator(expression)) {
if (isAssignmentOperator(expression)) {
return AssignmentTranslator.translate(expression, context());
}
if (operationDescriptor == null) {
if (isNotOverloadable()) {
return translateAsUnOverloadableBinaryOperation();
}
if (CompareToTranslator.isCompareToCall(expression, context())) {
if (isCompareToCall(expression, context())) {
return CompareToTranslator.translate(expression, context());
}
assert operationDescriptor != null :
"Overloadable operations must have not null descriptor";
if (isEquals(operationDescriptor)) {
return translateAsEqualsCall();
}
return CallTranslator.translate(expression, context());
}
private boolean isNotOverloadable() {
return operationDescriptor == null;
}
@NotNull
private JsExpression translateAsEqualsCall() {
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.DescriptorUtils.getVariableDescriptorForVariableAsFunction;
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.*;
/**
* @author Talanov Pavel
*/
//TODO: move translate() static method into builder (consider!)
//TODO: write tests on calling backing fields as functions
public final class CallTranslator extends AbstractTranslator {
@@ -38,14 +40,14 @@ public final class CallTranslator extends AbstractTranslator {
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), unaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor;
return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate();
return translate(context, receiver, arguments, (FunctionDescriptor) descriptor);
}
//TODO: method too long
public static JsExpression translate(@NotNull JetDotQualifiedExpression dotExpression,
@NotNull TranslationContext context) {
//TODO: look for duplication
JsExpression receiver = translateReceiver(context, dotExpression);
//TODO: util?
JetExpression selectorExpression = dotExpression.getSelectorExpression();
assert selectorExpression instanceof JetCallExpression;
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
@@ -53,7 +55,7 @@ public final class CallTranslator extends AbstractTranslator {
translateArgumentList(context, callExpression.getValueArguments());
FunctionDescriptor descriptor =
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,
@@ -62,18 +64,39 @@ public final class CallTranslator extends AbstractTranslator {
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
JsExpression receiver = getImplicitReceiver(context, descriptor);
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,
@NotNull TranslationContext context) {
JsExpression receiver = translateLeftExpression(context, binaryExpression);
List<JsExpression> arguments = Arrays.asList(translateRightExpression(context, binaryExpression));
//TODO: use PSI util method to get operation reference
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), binaryExpression.getOperationReference());
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
@@ -52,6 +52,10 @@ public final class PsiUtils {
return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
}
public static boolean isInOperation(@NotNull JetBinaryExpression binaryExpression) {
return binaryExpression.getOperationToken() == JetTokens.IN_KEYWORD;
}
@NotNull
/*package*/ static JetExpression getCallee(@NotNull JetCallExpression expression) {
JetExpression calleeExpression = expression.getCalleeExpression();
@@ -19,5 +19,15 @@ public final class RangeTest extends TranslationTest {
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;
}