]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/StringInterpolation.java
(refs #7250) Merging master, minor CHR bugfixes
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / constants / StringInterpolation.java
1 package org.simantics.scl.compiler.constants;
2
3 import java.util.Arrays;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.objectweb.asm.Label;
7 import org.objectweb.asm.Opcodes;
8 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
9 import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
10 import org.simantics.scl.compiler.internal.codegen.references.IVal;
11 import org.simantics.scl.compiler.internal.codegen.references.Val;
12 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
13 import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;
14 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
15 import org.simantics.scl.compiler.types.TVar;
16 import org.simantics.scl.compiler.types.Type;
17 import org.simantics.scl.compiler.types.Types;
18
19 public class StringInterpolation extends FunctionValue {
20
21     public static final TypeDesc STRING_BUILDER = TypeDesc.forClass(StringBuilder.class);
22     
23     private final String[] textParts;
24     
25     public StringInterpolation(Type[] parameterTypes, String[] textParts) {
26         super(TVar.EMPTY_ARRAY, Types.NO_EFFECTS, Types.STRING, parameterTypes);
27         this.textParts = textParts;
28     }
29     
30     public StringInterpolation(String[] textParts) {
31         this(stringTypeArray(textParts.length-1), textParts);
32     }
33     
34     @Override
35     public String toString() {
36         StringBuilder b = new StringBuilder();
37         b.append('"');
38         boolean first = true;
39         for(String textPart : textParts) {
40             if(first)
41                 first = false;
42             else
43                 b.append("\\(.)");
44             b.append(textPart);
45         }
46         b.append('"');
47         return b.toString();
48     }
49
50     private static Type[] stringTypeArray(int length) {
51         Type[] result = new Type[length];
52         for(int i=0;i<length;++i)
53             result[i] = Types.STRING;
54         return result;
55     }
56
57     @Override
58     public Type applyExact(MethodBuilder mb, Val[] parameters) {
59         if(textParts.length==1) {
60             mb.loadConstant(textParts[0]);
61         }
62         else if(textParts.length==2) {
63             if(parameters[0].getType() == Types.STRING) {
64                 // Optimized special cases "asd" + x, x + "asd"
65                 if(textParts[0].isEmpty()) {
66                     mb.push(parameters[0], Types.STRING);
67                     if(!textParts[1].isEmpty()) {
68                         mb.loadConstant(textParts[1]);
69                         mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
70                     }
71                     return Types.STRING;
72                 }
73                 else if(textParts[1].isEmpty()) {
74                     mb.loadConstant(textParts[0]);
75                     mb.push(parameters[0], Types.STRING);
76                     mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
77                     return Types.STRING;
78                 }
79             }
80         }
81         else if(textParts.length==3) {
82             if(parameters[0].getType() == Types.STRING && parameters[1].getType() == Types.STRING
83                     && textParts[0].isEmpty() && textParts[1].isEmpty() && textParts[2].isEmpty()) {
84                 mb.push(parameters[0], Types.STRING);
85                 mb.push(parameters[1], Types.STRING);
86                 mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
87                 return Types.STRING;
88             }
89         }
90         mb.newObject(STRING_BUILDER);
91         mb.dup();
92         mb.invokeConstructor(STRING_BUILDER, Constants.EMPTY_TYPEDESC_ARRAY);
93         
94         for(int i=0;i<parameters.length;++i) {
95             String textPart = textParts[i]; 
96             if(!textPart.isEmpty()) {
97                 mb.loadConstant(textPart);
98                 mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
99             }
100             
101             mb.push(parameters[i], parameterTypes[i]);
102             mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER,
103                     new TypeDesc[] {mb.getJavaTypeTranslator().toTypeDesc(parameterTypes[i])});
104         }
105         
106         {
107             String textPart = textParts[parameters.length]; 
108             if(!textPart.isEmpty()) {
109                 mb.loadConstant(textPart);
110                 mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
111             }
112         }
113         
114         mb.invokeVirtual(STRING_BUILDER, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
115         
116         return Types.STRING;
117     }
118     
119     @Override
120     public int hashCode() {
121         return Arrays.hashCode(textParts) ^ Arrays.hashCode(parameterTypes);
122     }
123
124     @Override
125     public boolean equals(Object obj) {
126         if(this == obj)
127             return true;
128         if(obj == null || obj.getClass() != getClass())
129             return false;
130         StringInterpolation other = (StringInterpolation)obj;
131         return Arrays.equals(textParts, other.textParts) && Arrays.equals(parameterTypes, other.parameterTypes);
132     }
133     
134     @Override
135     public int constructorTag() {
136         return textParts.length == 2 && parameterTypes[0] == Types.STRING ? 0 : -1;
137     }
138     
139     @Override
140     public void deconstruct(MethodBuilder mb, IVal parameter, Cont success, Label failure) {
141         if(textParts.length != 2)
142             throw new InternalCompilerError("String interpolation with more than one open string parts cannot be used as a pattern.");
143         mb.push(parameter, Types.STRING);
144         LocalVariable temp = mb.createLocalVariable(null, TypeDesc.STRING);
145         mb.storeLocal(temp);
146         if(!textParts[0].isEmpty()) {
147             mb.loadLocal(temp);
148             mb.loadConstant(textParts[0]);
149             mb.invokeVirtual("java/lang/String", "startsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
150             mb.ifZeroComparisonBranch(failure, "==");
151         }
152         if(!textParts[1].isEmpty()) {
153             mb.loadLocal(temp);
154             mb.loadConstant(textParts[1]);
155             mb.invokeVirtual("java/lang/String", "endsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
156             mb.ifZeroComparisonBranch(failure, "==");
157         }
158         mb.loadLocal(temp);
159         mb.loadConstant(textParts[0].length());
160         mb.loadLocal(temp);
161         mb.invokeVirtual("java/lang/String", "length", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
162         mb.loadConstant(textParts[1].length());
163         mb.math(Opcodes.ISUB);
164         mb.invokeVirtual("java/lang/String", "substring", TypeDesc.STRING, new TypeDesc[] {TypeDesc.INT, TypeDesc.INT});
165         mb.storeLocal(temp);
166         mb.jump(success, new LocalVariableConstant(Types.STRING, temp));
167     }
168     
169 }