prompto.statement.ForEachStatement Maven / Gradle / Ivy
The newest version!
package prompto.statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import prompto.compiler.ByteOperand;
import prompto.compiler.ClassConstant;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Flags;
import prompto.compiler.IInstructionListener;
import prompto.compiler.IVerifierEntry.VerifierType;
import prompto.compiler.InterfaceConstant;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.OffsetListenerConstant;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.StackLocal;
import prompto.compiler.StackState;
import prompto.error.PromptoError;
import prompto.expression.IExpression;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoString;
import prompto.parser.ICodeSection;
import prompto.parser.ISection;
import prompto.runtime.BreakResult;
import prompto.runtime.Context;
import prompto.runtime.Variable;
import prompto.transpiler.Transpiler;
import prompto.type.DictType;
import prompto.type.IType;
import prompto.type.IntegerType;
import prompto.type.ListType;
import prompto.utils.CodeWriter;
import prompto.value.IIterable;
import prompto.value.IValue;
import prompto.value.IntegerValue;
public class ForEachStatement extends BaseStatement {
Identifier v1, v2;
IExpression source;
StatementList statements;
public ForEachStatement(Identifier v1, Identifier v2, IExpression source, StatementList instructions) {
this.v1 = v1;
this.v2 = v2;
this.source = source;
this.statements = instructions;
}
public StatementList getInstructions() {
return statements;
}
@Override
public boolean canReturn() {
return true;
}
@Override
public ICodeSection locateCodeSection(ISection section) {
ICodeSection result = statements.locateCodeSection(section);
return result!=null ? result : super.locateCodeSection(section);
}
@Override
public void toDialect(CodeWriter writer) {
writer = writer.newChildWriter();
IType srcType = source.check(writer.getContext());
IType elemType = srcType.checkIterator(writer.getContext());
Identifier itemName = v2 == null ? v1 : v2;
writer.getContext().registerInstance(new Variable(itemName, elemType));
if (v2 != null)
writer.getContext().registerInstance(new Variable(v1, IntegerType.instance()));
switch(writer.getDialect()) {
case E:
toEDialect(writer);
break;
case O:
toODialect(writer);
break;
case M:
toMDialect(writer);
break;
}
}
private void toODialect(CodeWriter writer) {
writer.append("for each (").append(v1);
if(v2!=null)
writer.append(", ").append(v2);
writer.append(" in ");
source.toDialect(writer);
writer.append(")");
boolean oneLine = statements.size()==1 && (statements.get(0).isSimple());
if(!oneLine)
writer.append(" {");
writer.newLine().indent();
statements.toDialect(writer);
writer.dedent();
if(!oneLine)
writer.append("}").newLine();
}
private void toEDialect(CodeWriter writer) {
writer.append("for each ").append(v1);
if(v2!=null)
writer.append(", ").append(v2);
writer.append(" in ");
source.toDialect(writer);
writer.append(":").newLine().indent();
statements.toDialect(writer);
writer.dedent();
}
private void toMDialect(CodeWriter writer) {
writer.append("for ").append(v1);
if(v2!=null)
writer.append(", ").append(v2);
writer.append(" in ");
source.toDialect(writer);
writer.append(":").newLine().indent();
statements.toDialect(writer);
writer.dedent();
}
@Override
public IType check(Context context) {
IType srcType = source.check(context);
IType elemType = srcType.checkIterator(context);
return checkItemIterator(elemType, context);
}
private IType checkItemIterator(IType elemType, Context context) {
Context child = context.newChildContext();
Identifier itemName = v2 == null ? v1 : v2;
context.registerInstance(new Variable(itemName, elemType));
if (v2 != null)
context.registerInstance(new Variable(v1, IntegerType.instance()));
return statements.check(child, null);
}
@Override
public IValue interpret(Context context) throws PromptoError {
IType srcType = source.check(context);
IType elemType = srcType.checkIterator(context);
return interpretItemIterator(elemType, context);
}
private IValue interpretItemIterator(IType elemType, Context context) throws PromptoError {
if (v2 == null)
return interpretItemIteratorNoIndex(elemType, context);
else
return interpretItemIteratorWithIndex(elemType, context);
}
private IValue interpretItemIteratorNoIndex(IType elemType, Context context) throws PromptoError {
IValue src = source.interpret(context);
Iterator iterator = getIterator(context, src);
while (iterator.hasNext()) {
Context child = context.newChildContext();
child.registerInstance(new Variable(v1, elemType));
child.setValue(v1, iterator.next());
IValue value = statements.interpret(child);
if ( value == BreakResult.instance() )
break;
if (value != null)
return value;
}
return null;
}
private IValue interpretItemIteratorWithIndex(IType elemType, Context context) throws PromptoError {
IValue src = source.interpret(context);
Iterator iterator = getIterator(context, src);
long index = 0L;
while (iterator.hasNext()) {
Context child = context.newChildContext();
child.registerInstance(new Variable(v2, elemType));
child.setValue(v2, iterator.next());
child.registerInstance(new Variable(v1, IntegerType.instance()));
child.setValue(v1, new IntegerValue(++index));
IValue value = statements.interpret(child);
if (value != null)
return value;
}
return null;
}
@SuppressWarnings("unchecked")
private Iterator getIterator(Context context, Object src) {
// start with the most specialized implementation
if (src instanceof IIterable)
return ((IIterable) src).getIterable(context).iterator();
else if(src instanceof Iterable)
return ((Iterable)src).iterator();
else if(src instanceof Iterator)
return (Iterator)src;
else
throw new InternalError("Should never get there!");
}
@Override
public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
if(v2==null)
return compileWithoutIndex(context, method, flags);
else
return compileWithIndex(context, method, flags);
}
private ResultInfo compileWithIndex(Context context, MethodInfo method, Flags flags) {
List breakLoopListeners = new ArrayList<>();
flags = flags.withBreakLoopListeners(breakLoopListeners);
IType itemType = source.check(context).checkIterator(context);
java.lang.reflect.Type itemClass = itemType.toJavaType(context);
StackLocal iterLocal = compileIterator(context, method, flags);
StackLocal v1Local = compileInitCounter(method);
StackState iteratorState = method.captureStackState();
IInstructionListener test = method.addOffsetListener(new OffsetListenerConstant());
method.activateOffsetListener(test);
method.addInstruction(Opcode.GOTO, test);
IInstructionListener loop = method.addOffsetListener(new OffsetListenerConstant(true));
method.activateOffsetListener(loop);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
// call next and store in v2
CompilerUtils.compileALOAD(method, iterLocal);
InterfaceConstant m = new InterfaceConstant(Iterator.class, "next", Object.class);
method.addInstruction(Opcode.INVOKEINTERFACE, m);
method.addInstruction(Opcode.CHECKCAST, new ClassConstant(itemClass));
StackLocal v2Local = method.registerLocal(v2.toString(), VerifierType.ITEM_Object, new ClassConstant(itemClass));
method.addInstruction(Opcode.ASTORE, new ByteOperand((byte)v2Local.getIndex()));
// increment v1
compileIncrementCounter(method, v1Local);
// compile statements
context = context.newChildContext();
context.registerInstance(new Variable(v1, IntegerType.instance()));
context.registerInstance(new Variable(v2, itemType));
statements.compile(context, method, flags);
// done inner loop
method.unregisterLocal(v2Local);
method.addInstruction(Opcode.NOP); // avoid StackFrame collisions
// call hasNext
method.inhibitOffsetListener(test);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
CompilerUtils.compileALOAD(method, iterLocal);
m = new InterfaceConstant(Iterator.class, "hasNext", boolean.class);
method.addInstruction(Opcode.INVOKEINTERFACE, m);
// loop if not done
method.inhibitOffsetListener(loop);
method.addInstruction(Opcode.IFNE, loop);
method.unregisterLocal(v1Local);
method.unregisterLocal(iterLocal);
// add labels for break statements
if(!breakLoopListeners.isEmpty()) {
for(IInstructionListener listener : breakLoopListeners)
method.inhibitOffsetListener(listener);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
}
// TODO manage return value in loop
return new ResultInfo(void.class);
}
private void compileIncrementCounter(MethodInfo method, StackLocal local) {
compileLoadCounter(method, local);
method.addInstruction(Opcode.LCONST_1);
method.addInstruction(Opcode.LADD);
compileStoreCounter(method, local);
}
private void compileLoadCounter(MethodInfo method, StackLocal local) {
CompilerUtils.compileALOAD(method, local);
MethodConstant m = new MethodConstant(Long.class, "longValue", long.class);
method.addInstruction(Opcode.INVOKEVIRTUAL, m);
}
private StackLocal compileInitCounter(MethodInfo method) {
StackLocal local = method.registerLocal(v1.toString(), VerifierType.ITEM_Object, new ClassConstant(Long.class));
method.addInstruction(Opcode.LCONST_0);
compileStoreCounter(method, local);
return local;
}
private void compileStoreCounter(MethodInfo method, StackLocal local) {
MethodConstant m = new MethodConstant(Long.class, "valueOf", long.class, Long.class);
method.addInstruction(Opcode.INVOKESTATIC, m);
method.addInstruction(Opcode.ASTORE, new ByteOperand((byte)local.getIndex()));
}
private ResultInfo compileWithoutIndex(Context context, MethodInfo method, Flags flags) {
List breakLoopListeners = new ArrayList<>();
flags = flags.withBreakLoopListeners(breakLoopListeners);
IType itemType = source.check(context).checkIterator(context);
java.lang.reflect.Type itemClass = itemType.toJavaType(context);
StackLocal iterLocal = compileIterator(context, method, flags);
StackState iteratorState = method.captureStackState();
IInstructionListener test = method.addOffsetListener(new OffsetListenerConstant());
method.activateOffsetListener(test);
method.addInstruction(Opcode.GOTO, test);
IInstructionListener loop = method.addOffsetListener(new OffsetListenerConstant(true));
method.activateOffsetListener(loop);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
// call next and store in v1
CompilerUtils.compileALOAD(method, iterLocal);
InterfaceConstant m = new InterfaceConstant(Iterator.class, "next", Object.class);
method.addInstruction(Opcode.INVOKEINTERFACE, m);
method.addInstruction(Opcode.CHECKCAST, new ClassConstant(itemClass));
StackLocal v1Local = method.registerLocal(v1.toString(), VerifierType.ITEM_Object, new ClassConstant(itemClass));
method.addInstruction(Opcode.ASTORE, new ByteOperand((byte)v1Local.getIndex()));
// compile statements
context = context.newChildContext();
context.registerInstance(new Variable(v1, itemType));
statements.compile(context, method, flags);
// done inner loop
method.unregisterLocal(v1Local);
method.addInstruction(Opcode.NOP); // avoid StackFrame collisions
// call hasNext
method.inhibitOffsetListener(test);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
CompilerUtils.compileALOAD(method, iterLocal);
m = new InterfaceConstant(Iterator.class, "hasNext", boolean.class);
method.addInstruction(Opcode.INVOKEINTERFACE, m);
// loop if not done
method.inhibitOffsetListener(loop);
method.addInstruction(Opcode.IFNE, loop);
method.unregisterLocal(iterLocal);
// add labels for break statements
if(!breakLoopListeners.isEmpty()) {
for(IInstructionListener listener : breakLoopListeners)
method.inhibitOffsetListener(listener);
method.restoreFullStackState(iteratorState);
method.placeLabel(iteratorState);
}
// TODO manage return value in loop
return new ResultInfo(void.class);
}
private StackLocal compileIterator(Context context, MethodInfo method, Flags flags) {
ResultInfo info = source.compile(context, method, flags);
if(info.getType() == String.class) {
MethodConstant mc = new MethodConstant(PromptoString.class, "iterable", String.class, Iterable.class);
method.addInstruction(Opcode.INVOKESTATIC, mc);
}
InterfaceConstant ic = new InterfaceConstant(Iterable.class, "iterator", Iterator.class);
method.addInstruction(Opcode.INVOKEINTERFACE, ic);
String iterName = method.nextTransientName("iter");
StackLocal iterLocal = method.registerLocal(iterName, VerifierType.ITEM_Object, new ClassConstant(Iterator.class));
method.addInstruction(Opcode.ASTORE, new ByteOperand((byte)iterLocal.getIndex()), new ClassConstant(Iterator.class));
return iterLocal;
}
@Override
public void declare(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
if(srcType instanceof DictType)
transpiler.require("StrictSet");
IType elemType = srcType.checkIterator(transpiler.getContext());
this.source.declare(transpiler);
transpiler = transpiler.newChildTranspiler();
if(this.v2!=null) {
transpiler.getContext().registerInstance(new Variable(this.v1, IntegerType.instance()));
transpiler.getContext().registerInstance(new Variable(this.v2, elemType));
} else
transpiler.getContext().registerInstance(new Variable(this.v1, elemType));
this.statements.declare(transpiler);
}
@Override
public boolean transpile(Transpiler transpiler) {
if(this.v2!=null)
this.transpileWithIndex(transpiler);
else
this.transpileNoIndex(transpiler);
return true;
}
private void transpileNoIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
if(srcType instanceof ListType)
this.transpileArrayNoIndex(transpiler);
else
this.transpileIteratorNoIndex(transpiler);
}
private void transpileIteratorNoIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
IType elemType = srcType.checkIterator(transpiler.getContext());
String iterName = "$" + this.v1 + "_iterator";
transpiler.append("var ").append(iterName).append(" = ");
this.source.transpile(transpiler);
transpiler.append(".iterator();");
transpiler.newLine();
transpiler.append("while(").append(iterName).append(".hasNext()) {");
Transpiler child = transpiler.newChildTranspiler();
child.indent();
child.getContext().registerInstance(new Variable(this.v1, elemType));
child.append("var ").append(this.v1.toString()).append(" = ").append(iterName).append(".next();");
child.newLine();
this.statements.transpile(child);
child.dedent();
child.flush();
transpiler.append("}");
transpiler.newLine();
}
private void transpileArrayNoIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
IType elemType = srcType.checkIterator(transpiler.getContext());
String itemsName = "$" + this.v1 + "_items";
transpiler.append("var ").append(itemsName).append(" = ");
this.source.transpile(transpiler);
transpiler.append(";").newLine();
String idxName = "$" + this.v1 + "_idx";
transpiler.append("for(var ").append(idxName).append(" = 0; ").append(idxName).append(" < ").append(itemsName).append(".length; ").append(idxName).append("++) {");
Transpiler child = transpiler.newChildTranspiler();
child.indent();
child.getContext().registerInstance(new Variable(this.v1, elemType));
child.append("var ").append(this.v1.toString()).append(" = ").append(itemsName).append("[").append(idxName).append("];").newLine();
this.statements.transpile(child);
child.dedent();
child.flush();
transpiler.append("}");
transpiler.newLine();
}
private void transpileWithIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
if(srcType instanceof ListType)
this.transpileArrayWithIndex(transpiler);
else
this.transpileIteratorWithIndex(transpiler);
}
private void transpileIteratorWithIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
IType elemType = srcType.checkIterator(transpiler.getContext());
transpiler.append("var ").append(this.v1.toString()).append(" = 1;").newLine();
String iterName = "$" + this.v2 + "_iterator";
transpiler.append("var ").append(iterName).append(" = ");
this.source.transpile(transpiler);
transpiler.append(".iterator();");
transpiler.newLine();
transpiler.append("while(").append(iterName).append(".hasNext()) {");
Transpiler child = transpiler.newChildTranspiler();
child.indent();
child.getContext().registerInstance(new Variable(this.v1, IntegerType.instance()));
child.getContext().registerInstance(new Variable(this.v2, elemType));
child.append("var ").append(this.v2.toString()).append(" = ").append(iterName).append(".next();").newLine();
this.statements.transpile(child);
child.append(this.v1.toString()).append("++;").newLine();
child.dedent();
child.flush();
transpiler.append("}");
transpiler.newLine();
}
private void transpileArrayWithIndex(Transpiler transpiler) {
IType srcType = this.source.check(transpiler.getContext());
IType elemType = srcType.checkIterator(transpiler.getContext());
String itemsName = "$" + this.v2 + "_items";
transpiler.append("var ").append(itemsName).append(" = ");
this.source.transpile(transpiler);
transpiler.append(";").newLine();
transpiler.append("for(var ").append(this.v1.toString()).append(" = 1; ").append(this.v1.toString()).append(" <= ").append(itemsName).append(".length; ").append(this.v1.toString()).append("++) {");
Transpiler child = transpiler.newChildTranspiler();
child.indent();
child.getContext().registerInstance(new Variable(this.v1, IntegerType.instance()));
child.getContext().registerInstance(new Variable(this.v2, elemType));
child.append("var ").append(this.v2.toString()).append(" = ").append(itemsName).append("[").append(this.v1.toString()).append("-1];").newLine();
this.statements.transpile(child);
child.dedent();
child.flush();
transpiler.append("}");
transpiler.newLine();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy