prompto.expression.IteratorExpression Maven / Gradle / Ivy
The newest version!
package prompto.expression;
import java.lang.reflect.Type;
import prompto.compiler.ClassConstant;
import prompto.compiler.ClassFile;
import prompto.compiler.Descriptor;
import prompto.compiler.Flags;
import prompto.compiler.IVerifierEntry.VerifierType;
import prompto.compiler.InterfaceConstant;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.NamedType;
import prompto.compiler.ResultInfo;
import prompto.compiler.Tags;
import prompto.error.PromptoError;
import prompto.grammar.Identifier;
import prompto.intrinsic.IterableWithCounts;
import prompto.intrinsic.PromptoIterable;
import prompto.parser.CodeSection;
import prompto.runtime.Context;
import prompto.runtime.Variable;
import prompto.statement.ReturnStatement;
import prompto.statement.UnresolvedCall;
import prompto.transpiler.Transpiler;
import prompto.type.IType;
import prompto.type.IterableType;
import prompto.type.IteratorType;
import prompto.utils.CodeWriter;
import prompto.value.IIterable;
import prompto.value.IValue;
import prompto.value.IterableValue;
public class IteratorExpression extends CodeSection implements IExpression {
Identifier id;
IExpression source;
IExpression expression;
public IteratorExpression(Identifier id, IExpression source, IExpression exp) {
this.id = id;
this.source = source;
this.expression = exp;
}
@Override
public String toString() {
return expression.toString() +
" for each " +
id.toString() +
" in " +
source.toString();
}
@Override
public IteratorType check(Context context) {
IType srcType = source.check(context).checkIterator(context);
Context child = context.newChildContext();
child.registerInstance(new Variable(id, srcType));
IType resultType = expression.check(child);
return new IteratorType(resultType);
}
@Override
public IValue interpret(Context context) throws PromptoError {
IType srcType = source.check(context).checkIterator(context);
Context child = context.newChildContext();
child.registerInstance(new Variable(id, srcType));
IType resultType = expression.check(child);
IValue items = source.interpret(context);
IterableWithCounts iterable = getIterable(context, items);
return new IterableValue(context, id, srcType, iterable, expression, resultType);
}
@Override
public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
Type innerClassType = compileInnerClass(context, method.getClassFile());
// instantiate inner class
ClassConstant innerClass = new ClassConstant(innerClassType);
method.addInstruction(Opcode.NEW, innerClass);
method.addInstruction(Opcode.DUP);
// get iterable
ResultInfo srcinfo = source.compile(context, method, flags);
// get the length
method.addInstruction(Opcode.DUP);
if(srcinfo.isInterface()) {
InterfaceConstant c = new InterfaceConstant(srcinfo.getType(), "getNativeCount", long.class);
method.addInstruction(Opcode.INVOKEINTERFACE, c);
} else {
MethodConstant c = new MethodConstant(srcinfo.getType(), "getNativeCount", long.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, c);
}
// call the constructor
Descriptor.Method proto = new Descriptor.Method(Iterable.class, long.class, void.class);
MethodConstant m = new MethodConstant(innerClass, "", proto);
method.addInstruction(Opcode.INVOKESPECIAL, m);
// done
return new ResultInfo(IterableWithCounts.class);
}
private Type compileInnerClass(Context context, ClassFile parentClass) {
int innerClassIndex = 1 + parentClass.getInnerClasses().size();
String innerClassName = parentClass.getThisClass().getType().getTypeName() + '$' + innerClassIndex;
Type innerClassType = new NamedType(innerClassName);
ClassFile classFile = new ClassFile(innerClassType);
classFile.setSuperClass(new ClassConstant(PromptoIterable.class));
compileInnerClassConstructor(classFile);
compileInnerClassExpression(context, classFile);
parentClass.addInnerClass(classFile);;
return innerClassType;
}
private MethodInfo compileInnerClassConstructor(ClassFile classFile) {
Descriptor.Method proto = new Descriptor.Method(Iterable.class, long.class, void.class);
MethodInfo method = classFile.newMethod("", proto);
method.registerLocal("this", VerifierType.ITEM_UninitializedThis, classFile.getThisClass());
method.registerLocal("iterable", VerifierType.ITEM_Object, new ClassConstant(Iterable.class));
method.registerLocal("count", VerifierType.ITEM_Long, null);
method.addInstruction(Opcode.ALOAD_0, classFile.getThisClass());
method.addInstruction(Opcode.ALOAD_1, new ClassConstant(Iterable.class));
method.addInstruction(Opcode.LLOAD_2, new ClassConstant(long.class));
MethodConstant m = new MethodConstant(classFile.getSuperClass(), "", proto);
method.addInstruction(Opcode.INVOKESPECIAL, m);
method.addInstruction(Opcode.RETURN);
return method;
}
private void compileInnerClassExpression(Context context, ClassFile classFile) {
IType paramIType = source.check(context).checkIterator(context);
context = context.newChildContext();
context.registerInstance(new Variable(id, paramIType));
Type paramType = paramIType.toJavaType(context);
Type resultType = expression.check(context).toJavaType(context);
compileInnerClassBridgeMethod(classFile, paramType, resultType);
compileInnerClassApplyMethod(context, classFile, paramType, resultType);
}
private void compileInnerClassApplyMethod(Context context, ClassFile classFile, Type paramType, Type resultType) {
// create the "apply" method itself
Descriptor.Method proto = new Descriptor.Method(paramType, resultType);
MethodInfo method = classFile.newMethod("apply", proto);
method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());
method.registerLocal(id.toString(), VerifierType.ITEM_Object, new ClassConstant(paramType));
ReturnStatement stmt = new ReturnStatement(expression);
stmt.compile(context, method, new Flags());
}
private void compileInnerClassBridgeMethod(ClassFile classFile, Type paramType, Type resultType) {
// create a bridge "apply" method to convert Object -> paramType
Descriptor.Method proto = new Descriptor.Method(Object.class, Object.class);
MethodInfo method = classFile.newMethod("apply", proto);
method.addModifier(Tags.ACC_BRIDGE | Tags.ACC_SYNTHETIC);
method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());
method.registerLocal(id.toString(), VerifierType.ITEM_Object, new ClassConstant(Object.class));
method.addInstruction(Opcode.ALOAD_0, classFile.getThisClass());
method.addInstruction(Opcode.ALOAD_1, new ClassConstant(Object.class));
method.addInstruction(Opcode.CHECKCAST, new ClassConstant(paramType));
proto = new Descriptor.Method(paramType, resultType);
MethodConstant c = new MethodConstant(classFile.getThisClass(), "apply", proto);
method.addInstruction(Opcode.INVOKEVIRTUAL, c);
method.addInstruction(Opcode.ARETURN, new ClassConstant(resultType));
}
@SuppressWarnings("unchecked")
private IterableWithCounts getIterable(Context context, Object src) {
if (src instanceof IIterable)
return ((IIterable) src).getIterable(context);
else if(src instanceof IterableWithCounts)
return (IterableWithCounts)src;
else
throw new InternalError("Should never get there!");
}
@Override
public void toDialect(CodeWriter writer) {
IType srcType = source.check(writer.getContext()).checkIterator(writer.getContext());
writer = writer.newChildWriter();
writer.getContext().registerInstance(new Variable(id, srcType));
switch(writer.getDialect()) {
case E:
toEDialect(writer);
break;
case O:
toODialect(writer);
break;
case M:
toMDialect(writer);
break;
}
}
private void toMDialect(CodeWriter writer) {
IExpression expression = extractFromParenthesisIfPossible(this.expression);
expression.toDialect(writer);
writer.append(" for each ");
writer.append(id.toString());
writer.append(" in ");
source.toDialect(writer);
}
private void toODialect(CodeWriter writer) {
IExpression expression = extractFromParenthesisIfPossible(this.expression);
expression.toDialect(writer);
writer.append(" for each ( ");
writer.append(id.toString());
writer.append(" in ");
source.toDialect(writer);
writer.append(" )");
}
private void toEDialect(CodeWriter writer) {
IExpression expression = encloseInParenthesisIfRequired(this.expression);
expression.toDialect(writer);
writer.append(" for each ");
writer.append(id.toString());
writer.append(" in ");
source.toDialect(writer);
}
private static IExpression encloseInParenthesisIfRequired(IExpression expression) {
if(mustBeEnclosedInParenthesis(expression))
return new ParenthesisExpression(expression);
else
return expression;
}
private static IExpression extractFromParenthesisIfPossible(IExpression expression) {
if(expression instanceof ParenthesisExpression) {
IExpression enclosed = ((ParenthesisExpression)expression).getExpression();
if(mustBeEnclosedInParenthesis(enclosed))
return enclosed;
}
return expression;
}
private static boolean mustBeEnclosedInParenthesis(IExpression expression) {
return expression instanceof UnresolvedCall;
}
@Override
public void declare(Transpiler transpiler) {
this.source.declare(transpiler);
IType sourceType = this.source.check(transpiler.getContext());
if(sourceType instanceof IterableType)
sourceType.declareIterator(transpiler, this.id, this.expression);
else
transpiler.getContext().getProblemListener().reportExpectingCollection(this, sourceType);
}
@Override
public boolean transpile(Transpiler transpiler) {
IType sourceType = this.source.check(transpiler.getContext());
this.source.transpile(transpiler);
if(sourceType instanceof IterableType)
sourceType.transpileIterator(transpiler, this.id, this.expression);
else
transpiler.getContext().getProblemListener().reportExpectingCollection(this, sourceType);
return false;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy