use of dyvilx.tools.compiler.ast.pattern.Pattern in project Dyvil by Dyvil.
the class MatchCase method resolve.
public void resolve(MarkerList markers, IType type, IContext context) {
if (this.pattern != null) {
this.pattern = this.pattern.resolve(markers, context);
final Pattern typedPattern = this.pattern.withType(type, markers);
if (typedPattern == null) {
Marker marker = Markers.semanticError(this.pattern.getPosition(), "pattern.type.incompatible");
marker.addInfo(Markers.getSemantic("pattern.type", this.pattern.getType()));
marker.addInfo(Markers.getSemantic("value.type", type));
markers.add(marker);
} else {
this.pattern = typedPattern;
}
}
context = context.push(this);
if (this.condition != null) {
this.condition = this.condition.resolve(markers, context);
this.condition = TypeChecker.convertValue(this.condition, Types.BOOLEAN, null, markers, context, CONDITION_MARKER_SUPPLIER);
}
if (this.action != null) {
this.action = this.action.resolve(markers, context);
}
context.pop();
}
use of dyvilx.tools.compiler.ast.pattern.Pattern in project Dyvil by Dyvil.
the class MatchExpr method generateSwitch.
private void generateSwitch(MethodWriter writer, Object frameType) throws BytecodeException {
MatchCase defaultCase = null;
Label defaultLabel = null;
int cases = 0;
// the minimum int
int low = Integer.MAX_VALUE;
// the maximum int
int high = Integer.MIN_VALUE;
// Do we need to store the value in a variable (for string equality checks later)
boolean switchVar = false;
// generated.
for (int i = 0; i < this.caseCount; i++) {
MatchCase matchCase = this.cases[i];
Pattern pattern = matchCase.pattern;
if (switchVar || pattern.switchCheck()) {
switchVar = true;
}
if (pattern.isExhaustive()) {
defaultCase = matchCase;
defaultLabel = new Label();
continue;
}
int min = pattern.minValue();
if (min < low) {
low = min;
}
int max = pattern.maxValue();
if (max > high) {
high = max;
}
cases += pattern.subPatterns();
}
// Check if a match error should be generated - Non-exhaustive pattern
// and no default label
final Label endLabel = new Label();
Label matchErrorLabel = null;
if (!this.exhaustive) {
// Need a variable for MatchError
switchVar = true;
matchErrorLabel = new Label();
if (defaultLabel == null) {
defaultLabel = matchErrorLabel;
}
} else if (defaultLabel == null) {
// Exhaustive pattern - default label is end label
defaultLabel = endLabel;
}
final boolean expr = frameType != null;
// Write the value
final IType matchedType = this.matchedValue.getType();
final int localCount = writer.localCount();
final int varIndex;
if (switchVar) {
varIndex = this.matchedValue.writeStoreLoad(writer, null);
} else {
varIndex = -1;
this.matchedValue.writeExpression(writer, null);
}
if (Types.isSuperClass(Types.ENUM, matchedType)) {
// x.name().hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Enum", "name", "()Ljava/lang/String;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
} else if (Types.isSuperClass(Types.STRING, matchedType)) {
// x.hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
} else if (matchedType.getAnnotation(Types.SWITCHOPTIMIZED_CLASS) != null) {
// x.getClass().getName().hashCode()
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
writer.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
}
final int localCountInner = writer.localCount();
final KeyCache keyCache = new KeyCache(cases);
// Third run: fill the Key Cache
for (int i = 0; i < this.caseCount; i++) {
final MatchCase matchCase = this.cases[i];
final Pattern pattern = matchCase.pattern;
final int subPatterns = pattern.subPatterns();
for (int j = 0; j < subPatterns; j++) {
final Pattern subPattern = pattern.subPattern(j);
if (subPattern.isExhaustive()) {
continue;
}
final int switchValue = subPattern.switchValue();
keyCache.add(switchValue, matchCase, subPattern);
}
}
final Collection<KeyCache.Entry> entries = keyCache.uniqueEntries();
// Generate switch target labels
for (KeyCache.Entry topEntry : entries) {
for (KeyCache.Entry entry = topEntry; entry != null; entry = entry.next) {
entry.switchLabel = new Label();
}
}
// Choose and generate the appropriate instruction
if (useTableSwitch(low, high, cases)) {
this.writeTableSwitch(writer, entries, defaultLabel, low, high);
} else {
this.writeLookupSwitch(writer, entries, defaultLabel, cases);
}
// Fourth run: generate the target labels
for (KeyCache.Entry topEntry : entries) {
KeyCache.Entry entry = topEntry;
final int key = entry.key;
do {
final KeyCache.Entry next = entry.next;
final MatchCase matchCase = entry.matchCase;
final Pattern pattern = entry.pattern;
Label elseLabel;
if (next != null && next.key == key) {
elseLabel = next.switchLabel;
if (elseLabel == null) {
elseLabel = next.switchLabel = new Label();
}
} else {
elseLabel = defaultLabel;
}
writer.visitTargetLabel(entry.switchLabel);
if (pattern.switchCheck()) {
pattern.writeJumpOnMismatch(writer, varIndex, elseLabel);
}
if (matchCase.condition != null) {
matchCase.condition.writeInvJump(writer, elseLabel);
}
this.writeAction(writer, expr, frameType, matchCase.action);
writer.resetLocals(localCountInner);
if (!writer.hasReturn()) {
writer.visitJumpInsn(Opcodes.GOTO, endLabel);
}
entry = next;
} while (entry != null && entry.key == key);
}
// Default Case
if (defaultCase != null) {
writer.visitTargetLabel(defaultLabel);
if (defaultCase.pattern.switchCheck()) {
defaultCase.pattern.writeJumpOnMismatch(writer, varIndex, matchErrorLabel);
}
if (defaultCase.condition != null) {
defaultCase.condition.writeInvJump(writer, matchErrorLabel);
}
this.writeAction(writer, expr, frameType, defaultCase.action);
writer.resetLocals(localCountInner);
if (!writer.hasReturn()) {
writer.visitJumpInsn(Opcodes.GOTO, endLabel);
}
}
// Generate Match Error
if (matchErrorLabel != null) {
writer.visitLabel(matchErrorLabel);
this.writeMatchError(writer, varIndex, matchedType);
}
writer.visitLabel(endLabel);
writer.resetLocals(localCount);
}
use of dyvilx.tools.compiler.ast.pattern.Pattern in project Dyvil by Dyvil.
the class MatchExpr method check.
@Override
public void check(MarkerList markers, IContext context) {
if (this.matchedValue != null) {
this.matchedValue.check(markers, context);
}
final Set<Object> values = new HashSet<>(this.caseCount);
for (int i = 0; i < this.caseCount; i++) {
final MatchCase matchCase = this.cases[i];
final Pattern pattern = matchCase.pattern;
matchCase.check(markers, context);
for (int j = 0, subPatterns = pattern.subPatterns(); j < subPatterns; j++) {
final Pattern subPattern = pattern.subPattern(j);
final Object constantValue = subPattern.constantValue();
if (constantValue != null && !values.add(constantValue)) {
markers.add(Markers.semanticError(subPattern.getPosition(), "match.case.duplicate", constantValue));
}
}
}
}
use of dyvilx.tools.compiler.ast.pattern.Pattern in project Dyvil by Dyvil.
the class TupleSurrogate method withType.
@Override
public Pattern withType(IType type, MarkerList markers) {
final IClass tupleClass = TupleType.getTupleClass(this.patternCount);
if (tupleClass == null) {
return null;
}
final IType tupleType = tupleClass.getClassType();
if (!Types.isSuperClass(type, tupleType)) {
return null;
}
// reset type to recompute it later
this.tupleType = null;
final TypeParameterList typeParameters = tupleClass.getTypeParameters();
for (int i = 0; i < this.patternCount; i++) {
final IType elementType = Types.resolveTypeSafely(type, typeParameters.get(i));
final Pattern pattern = this.patterns[i];
final Pattern typedPattern = pattern.withType(elementType, markers);
if (typedPattern == null) {
final Marker marker = Markers.semanticError(pattern.getPosition(), "pattern.tuple.element.type");
marker.addInfo(Markers.getSemantic("pattern.type", pattern.getType()));
marker.addInfo(Markers.getSemantic("tuple.element.type", elementType));
markers.add(marker);
} else {
this.patterns[i] = typedPattern;
}
}
if (!Types.isSuperClass(tupleType, type)) {
return new TypeCheckPattern(this, type, tupleType);
}
return this;
}
use of dyvilx.tools.compiler.ast.pattern.Pattern in project Dyvil by Dyvil.
the class UnapplyPattern method withMethod.
protected boolean withMethod(IType matchedType, IValue methodCall, MarkerList markers) {
final IType type = NullableType.unapply(methodCall.getType());
final String className = type.getInternalName();
if (// return type must be a tuple type
!className.startsWith("dyvil/tuple/Tuple$Of")) {
return false;
}
final TupleType tupleType = type.extract(TupleType.class);
final TypeList typeArguments = tupleType.getArguments();
if (this.patternCount != typeArguments.size()) {
final Marker marker = Markers.semanticError(this.position, "pattern.unapply.count", this.type.toString());
marker.addInfo(Markers.getSemantic("pattern.unapply.count.pattern", this.patternCount));
marker.addInfo(Markers.getSemantic("pattern.unapply.count.class", typeArguments.size()));
markers.add(marker);
return true;
}
this.unapplyCall = methodCall;
for (int i = 0; i < this.patternCount; i++) {
final IType subType = typeArguments.get(i);
final Pattern pattern = this.patterns[i];
final Pattern typedPattern = pattern.withType(subType, markers);
if (typedPattern == null) {
final Marker marker = Markers.semanticError(this.position, "pattern.unapply.type");
marker.addInfo(Markers.getSemantic("pattern.type", pattern.getType()));
marker.addInfo(Markers.getSemantic("classparameter.type", subType));
markers.add(marker);
} else {
this.patterns[i] = typedPattern;
}
}
this.switchValue = getSwitchValue(matchedType, this.type);
return true;
}
Aggregations