Search in sources :

Example 1 with Pattern

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();
}
Also used : Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) Marker(dyvilx.tools.parsing.marker.Marker)

Example 2 with Pattern

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);
}
Also used : Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) Label(dyvilx.tools.asm.Label) IType(dyvilx.tools.compiler.ast.type.IType)

Example 3 with Pattern

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));
            }
        }
    }
}
Also used : Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) HashSet(dyvil.collection.mutable.HashSet)

Example 4 with Pattern

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;
}
Also used : TypeCheckPattern(dyvilx.tools.compiler.ast.pattern.TypeCheckPattern) AbstractPattern(dyvilx.tools.compiler.ast.pattern.AbstractPattern) TypeCheckPattern(dyvilx.tools.compiler.ast.pattern.TypeCheckPattern) Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) TypeParameterList(dyvilx.tools.compiler.ast.generic.TypeParameterList) IClass(dyvilx.tools.compiler.ast.classes.IClass) Marker(dyvilx.tools.parsing.marker.Marker) IType(dyvilx.tools.compiler.ast.type.IType)

Example 5 with Pattern

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;
}
Also used : AbstractPattern(dyvilx.tools.compiler.ast.pattern.AbstractPattern) Pattern(dyvilx.tools.compiler.ast.pattern.Pattern) TupleType(dyvilx.tools.compiler.ast.type.compound.TupleType) Marker(dyvilx.tools.parsing.marker.Marker) TypeList(dyvilx.tools.compiler.ast.type.TypeList) IType(dyvilx.tools.compiler.ast.type.IType)

Aggregations

Pattern (dyvilx.tools.compiler.ast.pattern.Pattern)6 IType (dyvilx.tools.compiler.ast.type.IType)3 Marker (dyvilx.tools.parsing.marker.Marker)3 AbstractPattern (dyvilx.tools.compiler.ast.pattern.AbstractPattern)2 HashSet (dyvil.collection.mutable.HashSet)1 Label (dyvilx.tools.asm.Label)1 IClass (dyvilx.tools.compiler.ast.classes.IClass)1 TypeParameterList (dyvilx.tools.compiler.ast.generic.TypeParameterList)1 TypeCheckPattern (dyvilx.tools.compiler.ast.pattern.TypeCheckPattern)1 TypeList (dyvilx.tools.compiler.ast.type.TypeList)1 TupleType (dyvilx.tools.compiler.ast.type.compound.TupleType)1