prompto.statement.SwitchErrorStatement Maven / Gradle / Ivy
The newest version!
package prompto.statement;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import prompto.compiler.ClassConstant;
import prompto.compiler.CompilerUtils;
import prompto.compiler.ExceptionHandler;
import prompto.compiler.FieldConstant;
import prompto.compiler.Flags;
import prompto.compiler.IVerifierEntry.VerifierType;
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.ExecutionError;
import prompto.error.PromptoError;
import prompto.expression.IExpression;
import prompto.expression.SymbolExpression;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoException;
import prompto.literal.ListLiteral;
import prompto.runtime.Context;
import prompto.runtime.ErrorVariable;
import prompto.transpiler.Transpiler;
import prompto.type.EnumeratedCategoryType;
import prompto.type.IType;
import prompto.type.TypeMap;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.utils.Logger;
import prompto.value.IValue;
public class SwitchErrorStatement extends BaseSwitchStatement {
private static Logger logger = new Logger();
Identifier errorId;
StatementList statements;
StatementList finallyStatements;
public SwitchErrorStatement(Identifier errorName, StatementList statements) {
this.errorId = errorName;
this.statements = statements;
}
public SwitchErrorStatement(Identifier errorName, StatementList statements,
SwitchCaseList handlers, StatementList anyStmts, StatementList finalStmts) {
super(handlers, anyStmts);
this.errorId = errorName;
this.statements = statements;
this.finallyStatements = finalStmts;
}
public void setAlwaysInstructions(StatementList list) {
finallyStatements = list;
}
@Override
public void toDialect(CodeWriter writer) {
writer = writer.newLocalWriter();
writer.getContext().registerInstance(new ErrorVariable(errorId));
super.toDialect(writer);
}
@Override
protected void toODialect(CodeWriter writer) {
writer.append("try (");
writer.append(errorId);
writer.append(") {\n");
writer.indent();
statements.toDialect(writer);
writer.dedent();
writer.append("} ");
for(SwitchCase sc : switchCases)
sc.catchToODialect(writer);
if(defaultCase!=null) {
writer.append("catch(any) {\n");
writer.indent();
defaultCase.toDialect(writer);
writer.dedent();
writer.append("}");
}
if(finallyStatements!=null) {
writer.append("finally {\n");
writer.indent();
finallyStatements.toDialect(writer);
writer.dedent();
writer.append("}");
}
writer.newLine();
}
@Override
protected void toMDialect(CodeWriter writer) {
writer.append("try ");
writer.append(errorId);
writer.append(":\n");
writer.indent();
statements.toDialect(writer);
writer.dedent();
for(SwitchCase sc : switchCases)
sc.catchToPDialect(writer);
if(defaultCase!=null) {
writer.append("except:\n");
writer.indent();
defaultCase.toDialect(writer);
writer.dedent();
}
if(finallyStatements!=null) {
writer.append("finally:\n");
writer.indent();
finallyStatements.toDialect(writer);
writer.dedent();
}
writer.newLine();
}
@Override
protected void toEDialect(CodeWriter writer) {
writer.append("switch on ");
writer.append(errorId);
writer.append(" doing:\n");
writer.indent();
statements.toDialect(writer);
writer.dedent();
for(SwitchCase sc : switchCases)
sc.catchToEDialect(writer);
if(defaultCase!=null) {
writer.append("when any:\n");
writer.indent();
defaultCase.toDialect(writer);
writer.dedent();
}
if(finallyStatements!=null) {
writer.append("always:\n");
writer.indent();
finallyStatements.toDialect(writer);
writer.dedent();
}
}
@Override
protected void checkSwitchCasesType(Context context) {
Context child = context.newChildContext();
child.registerInstance(new ErrorVariable(errorId));
super.checkSwitchCasesType(child);
}
@Override
IType checkSwitchType(Context context) {
return new EnumeratedCategoryType(new Identifier("Error"));
}
@Override
protected void collectReturnTypes(Context context, TypeMap types) {
IType type = statements.check(context, null);
if(type!=VoidType.instance())
types.add(type);
Context child = context.newChildContext();
child.registerInstance(new ErrorVariable(errorId));
super.collectReturnTypes(child, types);
if(finallyStatements!=null) {
type = finallyStatements.check(context, null);
if(type!=VoidType.instance())
types.add(type);
}
}
@Override
public IValue interpret(Context context) throws PromptoError {
IValue result = null;
try {
result = statements.interpret(context);
} catch (ExecutionError e) {
logger.error(()->"While interpreting try/catch body", e);
IValue switchValue = e.interpret(context, errorId);
result = interpretSwitch(context, switchValue, e);
} catch(Throwable t) {
logger.error(()->"While interpreting try/catch body", t);
if(defaultCase!=null)
return defaultCase.interpret(context);
} finally {
if(finallyStatements!=null)
finallyStatements.interpret(context);
}
return result;
}
@Override
public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
List> handlers = installExceptionHandlers(context, method, flags);
ResultInfo result = statements.compile(context, method, flags);
if(result.isReturn() || result.isThrow())
compileExceptionHandlers(context, method, flags, handlers, null);
else {
List finalOffsets = new LinkedList<>();
StackState neutral = method.captureStackState();
OffsetListenerConstant finalOffset = method.addOffsetListener(new OffsetListenerConstant());
method.activateOffsetListener(finalOffset);
finalOffsets.add(finalOffset);
method.addInstruction(Opcode.GOTO, finalOffset);
compileExceptionHandlers(context, method, flags, handlers, finalOffsets);
finalOffsets.forEach((o)->
method.inhibitOffsetListener(o));
method.restoreFullStackState(neutral);
method.placeLabel(neutral);
}
return result;
}
private void compileExceptionHandlers(Context context, MethodInfo method,
Flags flags, List> handlerList, List finalOffsets ) {
Iterator iterCases = switchCases.iterator();
Iterator> iterHandler = handlerList.iterator();
if(iterCases.hasNext())
compileExceptionHandler(context, method, flags, iterCases.next(), iterHandler.next(), finalOffsets);
if(defaultCase!=null)
compileExceptionHandler(context, method, flags, null, iterHandler.next(), finalOffsets);
// TODO 'finally'
}
private void compileExceptionHandler(Context context, MethodInfo method, Flags flags,
SwitchCase switchCase, List handlers,
List finalOffsets) {
handlers.forEach((h)->
method.inhibitOffsetListener(h));
ExceptionHandler handler = makeCommonExceptionHandler(handlers);
method.placeExceptionHandler(handler);
Type exception = compileConvertException(context, method, flags, handler);
StackLocal error = method.registerLocal(errorId.toString(),
VerifierType.ITEM_Object, new ClassConstant(exception));
CompilerUtils.compileASTORE(method, error);
Context child = context.newChildContext();
child.registerInstance(new ErrorVariable(errorId));
ResultInfo result = switchCase!=null ?
switchCase.statements.compile(child, method, flags) :
defaultCase.compile(context, method, flags);
if(finalOffsets!=null && !result.isReturn() && !result.isThrow()) {
OffsetListenerConstant finalOffset = method.addOffsetListener(new OffsetListenerConstant());
method.activateOffsetListener(finalOffset);
finalOffsets.add(finalOffset);
method.addInstruction(Opcode.GOTO, finalOffset);
}
method.unregisterLocal(error);
}
private Type compileConvertException(Context context, MethodInfo method, Flags flags, String name) {
method.addInstruction(Opcode.POP); // the original exception
Type classType = CompilerUtils.getCategoryEnumConcreteType("Error");
ClassConstant cc = new ClassConstant(classType);
Type fieldType = CompilerUtils.getExceptionType(classType, name);
FieldConstant fc = new FieldConstant(cc, name, fieldType);
method.addInstruction(Opcode.GETSTATIC, fc);
return cc.getType();
}
private ExceptionHandler makeCommonExceptionHandler(List handlers) {
if(handlers.size()==1)
return handlers.get(0);
else
throw new UnsupportedOperationException();
}
private List> installExceptionHandlers(Context context,
MethodInfo method, Flags flags) {
List> handlers = new LinkedList<>();
for(SwitchCase sc : switchCases)
handlers.add(installExceptionHandlers(context, method, flags, sc));
if(defaultCase!=null)
handlers.add(installDefaultExceptionHandlers(context, method, flags));
// TODO 'finally'
return handlers;
}
private List installDefaultExceptionHandlers(Context context, MethodInfo method, Flags flags) {
List list = new LinkedList();
list.add(installExceptionHandler(context, method, flags, (SymbolExpression)null));
return list;
}
private List installExceptionHandlers(Context context, MethodInfo method,
Flags flags, SwitchCase sc) {
if(sc instanceof AtomicSwitchCase)
return installExceptionHandler(context, method, flags, (AtomicSwitchCase)sc);
else if(sc instanceof CollectionSwitchCase)
return installExceptionHandlers(context, method, flags, (CollectionSwitchCase)sc);
else
throw new UnsupportedOperationException();
}
private List installExceptionHandler(Context context, MethodInfo method,
Flags flags, AtomicSwitchCase sc) {
IExpression exp = sc.getExpression();
if(exp instanceof SymbolExpression) {
List list = new LinkedList();
list.add(installExceptionHandler(context, method, flags, (SymbolExpression)exp));
return list;
} else
throw new UnsupportedOperationException();
}
private List installExceptionHandlers(Context context, MethodInfo method,
Flags flags, CollectionSwitchCase sc) {
IExpression exp = sc.getExpression();
if(exp instanceof ListLiteral) {
List list = new LinkedList();
for(IExpression item : ((ListLiteral)exp).getExpressions()) {
if(item instanceof SymbolExpression)
list.add(installExceptionHandler(context, method, flags, (SymbolExpression)item));
else
throw new UnsupportedOperationException();
}
return list;
} else
throw new UnsupportedOperationException();
}
private Type compileConvertException(Context context, MethodInfo method, Flags flags, ExceptionHandler handler) {
Type type = handler.getException().getType();
if(type instanceof Class) {
String simpleName = PromptoException.getExceptionTypeName((Class>)type);
if(type.getTypeName().endsWith(simpleName))
return type;
else
return compileConvertException(context, method, flags, simpleName);
} else
return type;
}
private ExceptionHandler installExceptionHandler(Context context, MethodInfo method,
Flags flags, SymbolExpression symbol) {
Type type = getExceptionType(context, symbol);
ExceptionHandler handler = method.registerExceptionHandler(type);
method.activateOffsetListener(handler);
return handler;
}
private Type getExceptionType(Context context, SymbolExpression symbol) {
if(symbol==null)
return CompilerUtils.getCategoryEnumConcreteType("Error");
else {
Type type = PromptoException.getExceptionType(symbol.getName());
if(type!=null)
return type;
else
return symbol.getJavaType(context);
}
}
@Override
public void declare(Transpiler transpiler) {
transpiler.require("NativeError");
this.statements.declare(transpiler);
Transpiler child = transpiler.newChildTranspiler();
child.getContext().registerInstance(new ErrorVariable(this.errorId));
this.declareSwitch(child);
}
@Override
public boolean transpile(Transpiler transpiler) {
transpiler.append("try {").indent();
this.statements.transpile(transpiler);
transpiler.dedent().append("} catch(").append(this.errorId.toString()).append(") {").indent();
Transpiler child = transpiler.newChildTranspiler();
child.getContext().registerInstance(new ErrorVariable(this.errorId));
child.append("switch(translateError(").append(this.errorId.toString()).append(")) {").indent();
this.switchCases.forEach(switchCase -> {
switchCase.transpileError(child);
});
if(this.defaultCase!=null) {
child.append("default:").indent();
this.defaultCase.transpile(child);
child.dedent();
}
child.dedent().append("}");
if(this.finallyStatements!=null) {
child.append(" finally {").indent();
this.finallyStatements.transpile(child);
child.dedent().append("}");
}
child.dedent().append("}");
child.flush();
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy