// ex: se sts=4 sw=4 expandtab:
/*
* Yeti language compiler java bytecode generator.
*
* Copyright (c) 2007-2010 Madis Janson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package yeti.lang.compiler;
import yeti.renamed.asmx.*;
import java.util.*;
abstract class CasePattern implements Opcodes {
static final CasePattern ANY_PATTERN = new CasePattern() {
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
if (!preserve) {
ctx.insn(POP);
}
}
boolean irrefutable() {
return true;
}
};
int preparePattern(Ctx ctx) {
return 1;
}
abstract void tryMatch(Ctx ctx, Label onFail, boolean preserve);
boolean irrefutable() {
return false;
}
}
final class BindPattern extends CasePattern implements Binder {
private CaseExpr caseExpr;
private int nth;
BindRef param = new BindRef() {
void gen(Ctx ctx) {
ctx.load(caseExpr.paramStart + nth);
}
};
BindPattern(CaseExpr caseExpr, YType type) {
this.caseExpr = caseExpr;
param.binder = this;
param.type = type;
nth = caseExpr.paramCount++;
}
public BindRef getRef(int line) {
return param;
}
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
if (preserve) {
ctx.insn(DUP);
}
ctx.varInsn(ASTORE, caseExpr.paramStart + nth);
}
boolean irrefutable() {
return true;
}
}
final class ConstPattern extends CasePattern {
Code v;
ConstPattern(Code value) {
v = value;
}
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
if (preserve) {
ctx.insn(DUP);
}
v.gen(ctx);
ctx.methodInsn(INVOKEVIRTUAL, "java/lang/Object",
"equals", "(Ljava/lang/Object;)Z");
ctx.jumpInsn(IFEQ, onFail);
}
}
abstract class AListPattern extends CasePattern {
static final CasePattern EMPTY_PATTERN = new CasePattern() {
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
ctx.insn(DUP);
Label cont = new Label();
ctx.jumpInsn(IFNULL, cont);
if (preserve) {
ctx.insn(DUP);
}
ctx.typeInsn(CHECKCAST, "yeti/lang/AIter");
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AIter",
"isEmpty", "()Z");
ctx.jumpInsn(IFEQ, onFail);
if (preserve) {
ctx.visitLabel(cont);
} else {
Label end = new Label();
ctx.jumpInsn(GOTO, end);
ctx.visitLabel(cont);
ctx.insn(POP);
ctx.visitLabel(end);
}
}
};
abstract boolean listMatch(Ctx ctx, Label onFail, Label dropFail);
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
Label dropFail = preserve ? onFail : new Label();
ctx.insn(DUP);
ctx.jumpInsn(IFNULL, dropFail);
ctx.typeInsn(CHECKCAST, "yeti/lang/AList");
ctx.insn(DUP);
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AList",
"isEmpty", "()Z");
ctx.jumpInsn(IFNE, dropFail);
if (preserve) {
ctx.insn(DUP);
dropFail = new Label();
}
if (listMatch(ctx, onFail, dropFail) || !preserve) {
Label cont = new Label();
ctx.jumpInsn(GOTO, cont);
ctx.visitLabel(dropFail);
ctx.insn(POP);
ctx.jumpInsn(GOTO, onFail);
ctx.visitLabel(cont);
}
}
}
final class ConsPattern extends AListPattern {
private CasePattern hd;
private CasePattern tl;
ConsPattern(CasePattern hd, CasePattern tl) {
this.hd = hd;
this.tl = tl;
}
boolean listMatch(Ctx ctx, Label onFail, Label dropFail) {
if (hd != ANY_PATTERN) {
if (tl != ANY_PATTERN) {
ctx.insn(DUP);
} else {
dropFail = onFail;
}
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AList",
"first", "()Ljava/lang/Object;");
hd.preparePattern(ctx);
hd.tryMatch(ctx, dropFail, false);
}
if (tl != ANY_PATTERN) {
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AList",
"rest", "()Lyeti/lang/AList;");
tl.preparePattern(ctx);
tl.tryMatch(ctx, onFail, false);
} else if (hd == ANY_PATTERN) {
ctx.insn(POP);
}
return dropFail != onFail && !hd.irrefutable();
}
}
final class ListPattern extends AListPattern {
private CasePattern[] items;
ListPattern(CasePattern[] items) {
this.items = items;
}
boolean listMatch(Ctx ctx, Label onFail, Label dropFail) {
boolean dropUsed = false;
for (int i = 0; i < items.length; ++i) {
if (i != 0) {
ctx.insn(DUP);
ctx.jumpInsn(IFNULL, dropFail);
dropUsed = true;
}
if (items[i] != ANY_PATTERN) {
ctx.insn(DUP);
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AIter",
"first", "()Ljava/lang/Object;");
items[i].preparePattern(ctx);
items[i].tryMatch(ctx, dropFail, false);
dropUsed |= !items[i].irrefutable();
}
ctx.methodInsn(INVOKEVIRTUAL, "yeti/lang/AIter",
"next", "()Lyeti/lang/AIter;");
}
ctx.jumpInsn(IFNONNULL, onFail);
return dropUsed;
}
}
final class StructPattern extends CasePattern {
private String[] names;
private CasePattern[] patterns;
StructPattern(String[] names, CasePattern[] patterns) {
this.names = names;
this.patterns = patterns;
}
int preparePattern(Ctx ctx) {
return 1;
}
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
boolean dropped = false;
Label failed = preserve ? onFail : new Label();
for (int i = 0; i < names.length; ++i) {
if (patterns[i] == ANY_PATTERN)
continue;
if (preserve || i != names.length - 1)
ctx.insn(DUP);
else dropped = true;
ctx.ldcInsn(names[i]);
ctx.methodInsn(INVOKEINTERFACE, "yeti/lang/Struct",
"get", "(Ljava/lang/String;)Ljava/lang/Object;");
patterns[i].preparePattern(ctx);
patterns[i].tryMatch(ctx, i < names.length - 1
? failed : onFail, false);
}
if (!preserve && names.length > 1) {
Label ok = new Label();
ctx.jumpInsn(GOTO, ok);
ctx.visitLabel(failed);
ctx.insn(POP);
ctx.jumpInsn(GOTO, onFail);
ctx.visitLabel(ok);
if (!dropped)
ctx.insn(POP);
}
}
}
final class VariantPattern extends CasePattern {
String variantTag;
CasePattern variantArg;
VariantPattern(String tagName, CasePattern arg) {
variantTag = tagName;
variantArg = arg;
}
int preparePattern(Ctx ctx) {
ctx.typeInsn(CHECKCAST, "yeti/lang/Tag");
ctx.insn(DUP);
ctx.fieldInsn(GETFIELD, "yeti/lang/Tag", "name",
"Ljava/lang/String;");
return 2; // TN
}
void tryMatch(Ctx ctx, Label onFail, boolean preserve) {
Label jumpTo = onFail;
if (preserve) {
ctx.insn(DUP); // TNN
ctx.ldcInsn(variantTag);
ctx.jumpInsn(IF_ACMPNE, onFail); // TN
if (variantArg == ANY_PATTERN) {
return;
}
ctx.insn(SWAP); // NT
ctx.insn(DUP_X1); // TNT
} else if (variantArg == ANY_PATTERN) {
ctx.insn(SWAP); // NT
ctx.insn(POP); // N
ctx.ldcInsn(variantTag);
ctx.jumpInsn(IF_ACMPNE, onFail);
return;
} else {
Label cont = new Label(); // TN
ctx.ldcInsn(variantTag);
ctx.jumpInsn(IF_ACMPEQ, cont); // T
ctx.insn(POP);
ctx.jumpInsn(GOTO, onFail);
ctx.visitLabel(cont);
}
ctx.fieldInsn(GETFIELD, "yeti/lang/Tag", "value",
"Ljava/lang/Object;"); // TNt (t)
variantArg.preparePattern(ctx);
variantArg.tryMatch(ctx, onFail, false); // TN ()
}
}
final class CaseExpr extends Code {
private int totalParams;
private Code caseValue;
private List choices = new ArrayList();
int paramStart;
int paramCount;
CaseExpr(Code caseValue) {
this.caseValue = caseValue;
}
private static final class Choice {
CasePattern pattern;
Code expr;
}
void resetParams() {
if (totalParams < paramCount) {
totalParams = paramCount;
}
paramCount = 0;
}
void addChoice(CasePattern pattern, Code code) {
Choice c = new Choice();
c.pattern = pattern;
c.expr = code;
choices.add(c);
}
void gen(Ctx ctx) {
caseValue.gen(ctx);
paramStart = ctx.localVarCount;
ctx.localVarCount += totalParams;
Label next = null, end = new Label();
CasePattern lastPattern = ((Choice) choices.get(0)).pattern;
int patternStack = lastPattern.preparePattern(ctx);
for (int last = choices.size() - 1, i = 0; i <= last; ++i) {
Choice c = (Choice) choices.get(i);
if (lastPattern.getClass() != c.pattern.getClass()) {
ctx.popn(patternStack - 1);
patternStack = c.pattern.preparePattern(ctx);
}
lastPattern = c.pattern;
next = new Label();
c.pattern.tryMatch(ctx, next, true);
ctx.popn(patternStack);
c.expr.gen(ctx);
ctx.jumpInsn(GOTO, end);
ctx.visitLabel(next);
}
ctx.visitLabel(next);
ctx.popn(patternStack - 1);
ctx.methodInsn(INVOKESTATIC, "yeti/lang/Core",
"badMatch", "(Ljava/lang/Object;)Ljava/lang/Object;");
ctx.visitLabel(end);
}
void markTail() {
for (int i = choices.size(); --i >= 0;) {
((Choice) choices.get(i)).expr.markTail();
}
}
}