All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prompto.statement.IfStatement Maven / Gradle / Ivy

The newest version!
package prompto.statement;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import prompto.compiler.CompilerUtils;
import prompto.compiler.Flags;
import prompto.compiler.IInstructionListener;
import prompto.compiler.MethodInfo;
import prompto.compiler.OffsetListenerConstant;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.ResultInfo.Flag;
import prompto.compiler.StackState;
import prompto.error.PromptoError;
import prompto.expression.EqualsExpression;
import prompto.expression.IExpression;
import prompto.parser.CodeSection;
import prompto.parser.ICodeSection;
import prompto.runtime.Context;
import prompto.transpiler.Transpiler;
import prompto.type.BooleanType;
import prompto.type.IType;
import prompto.type.TypeMap;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.value.BooleanValue;
import prompto.value.IValue;

public class IfStatement extends BaseStatement {

	public static class IfElementList extends LinkedList {

		private static final long serialVersionUID = 1L;

		public IfElementList() {
		}

		public IfElementList(IfElement elem) {
			this.add(elem);
		}

	}

	IfElementList elements = new IfElementList();

	public IfStatement(IExpression condition, StatementList statements) {
		elements.add(new IfElement(condition, statements));
	}

	public IfStatement(IExpression condition, StatementList statements, IfElementList elseIfs,
			StatementList elseStmts) {
		elements.add(new IfElement(condition, statements));
		if (elseIfs != null)
			elements.addAll(elseIfs);
		if (elseStmts != null)
			elements.add(new IfElement(null, elseStmts));
	}

	public void addAdditional(IExpression condition, StatementList instructions) {
		elements.add(new IfElement(condition, instructions));
	}

	public void setFinal(StatementList instructions) {
		elements.add(new IfElement(null, instructions));
	}

	@Override
	public boolean canReturn() {
		return true;
	}

	@Override
	public void toDialect(CodeWriter writer) {
		switch (writer.getDialect()) {
		case E:
			toEDialect(writer);
			break;
		case O:
			toODialect(writer);
			break;
		case M:
			toMDialect(writer);
			break;
		}
	}

	private void toMDialect(CodeWriter writer) {
		boolean first = true;
		for (IfElement elem : elements) {
			if (!first)
				writer.append("else ");
			elem.toDialect(writer);
			first = false;
		}
		writer.newLine();
	}

	private void toODialect(CodeWriter writer) {
		boolean first = true;
		boolean curly = false;
		for (IfElement elem : elements) {
			if (!first) {
				if (curly)
					writer.append(" ");
				writer.append("else ");
			}
			curly = elem.toDialect(writer);
			first = false;
		}
		writer.newLine();
	}

	private void toEDialect(CodeWriter writer) {
		boolean first = true;
		for (IfElement elem : elements) {
			if (!first)
				writer.append("else ");
			elem.toDialect(writer);
			first = false;
		}
		writer.newLine();
	}

	@Override
	public IType check(Context context) {
		TypeMap types = new TypeMap();
		ICodeSection section = null;
		for(IfElement element : elements) {
			IType type = element.check(context);
			if(type!=VoidType.instance()) {
				section = element;
				types.add(type);
			}
		}
		return types.inferType(context, section);
	}

	@Override
	public IValue interpret(Context context) throws PromptoError {
		for (IfElement element : elements) {
			IExpression condition = element.getCondition();
			Object test = condition == null ? BooleanValue.TRUE : condition.interpret(context);
			if (BooleanValue.TRUE == test)
				return element.interpret(context);
		}
		return null;
	}

	@Override
	public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
		IType resultType = check(context);
		compileIfElements(context, method, flags);
		return new ResultInfo(resultType.toJavaType(context)).withFlag(Flag.RETURN, resultType!=VoidType.instance());
	}

	static class IfElementBranch {
		List finalOffsetListeners = new ArrayList<>();
		IInstructionListener branchOffsetListener = null;
		StackState neutralState = null;
	}

	private void compileIfElements(Context context, MethodInfo method, Flags flags) {
		IfElementBranch branch = new IfElementBranch();
		branch.neutralState = method.captureStackState();
		for (IfElement element : elements)
			compileIfElement(context, method, flags, element, branch);
		method.restoreFullStackState(branch.neutralState);
		method.placeLabel(branch.neutralState);
		stopListeningForThisBranch(method, branch);
		branch.finalOffsetListeners.forEach((l) -> method.inhibitOffsetListener(l));
	}

	private void compileIfElement(Context context, MethodInfo method, Flags flags, IfElement element,
			IfElementBranch branch) {
		restoreNeutralStackState(method, branch);
		stopListeningForThisBranch(method, branch);
		compileCondition(context, method, flags, element);
		startListeningForNextBranch(method, element, branch);
		compileBranch(method, element, branch);
		context = prepareAutodowncast(context, method, element);
		ResultInfo info = compileStatements(context, method, flags, element, branch);
		startListeningForFinalThenGoto(context, method, flags, element, branch, info);
		cancelAutodowncast(context, method, element);
	}

	private void cancelAutodowncast(Context context, MethodInfo method, IfElement element) {
		if (element.condition instanceof EqualsExpression)
			((EqualsExpression) element.condition).cancelAutodowncast(context, method);
	}

	private Context prepareAutodowncast(Context context, MethodInfo method, IfElement element) {
		if (element.condition instanceof EqualsExpression)
			return ((EqualsExpression) element.condition).prepareAutodowncast(context, method);
		else
			return context;
	}

	private void compileBranch(MethodInfo method, IfElement element, IfElementBranch branch) {
		if (element.condition != null) {
			method.addInstruction(Opcode.IFEQ, branch.branchOffsetListener);
		}
	}

	private void startListeningForFinalThenGoto(Context context, MethodInfo method, Flags flags, IfElement element,
			IfElementBranch branch, ResultInfo info) {
		if (element.condition == null || info.isReturn() || info.isBreak())
			return;
		IInstructionListener finalOffset = method.addOffsetListener(new OffsetListenerConstant());
		method.activateOffsetListener(finalOffset);
		branch.finalOffsetListeners.add(finalOffset);
		method.addInstruction(Opcode.GOTO, finalOffset);
	}

	private ResultInfo compileStatements(Context context, MethodInfo method, Flags flags, IfElement element,
			IfElementBranch branch) {
		if (element.statements != null)
			return element.statements.compile(context, method, flags);
		else
			return new ResultInfo(void.class);
	}

	private void stopListeningForThisBranch(MethodInfo method, IfElementBranch branch) {
		if (branch.branchOffsetListener != null) {
			method.inhibitOffsetListener(branch.branchOffsetListener);
			branch.branchOffsetListener = null;
		}
	}

	private void startListeningForNextBranch(MethodInfo method, IfElement element, IfElementBranch branch) {
		if (element.condition != null) {
			branch.branchOffsetListener = method.addOffsetListener(new OffsetListenerConstant());
			method.activateOffsetListener(branch.branchOffsetListener);
		}
	}

	private void compileCondition(Context context, MethodInfo method, Flags flags, IfElement element) {
		if (element.condition != null) {
			ResultInfo info = element.condition.compile(context, method, flags.withPrimitive(true));
			if (java.lang.Boolean.class == info.getType())
				CompilerUtils.BooleanToboolean(method);
		}
	}

	private void restoreNeutralStackState(MethodInfo method, IfElementBranch branch) {
		// is there a need to restore the stack?
		if (branch.branchOffsetListener != null) {
			method.restoreFullStackState(branch.neutralState);
			method.placeLabel(branch.neutralState);
		}
	}

	@Override
	public void declare(Transpiler transpiler) {
		this.elements.forEach(e -> e.declare(transpiler));
	}

	@Override
	public boolean transpile(Transpiler transpiler) {
		for (int i = 0; i < this.elements.size(); i++) {
			IfElement element = this.elements.get(i);
			if (i > 0)
				transpiler.append(" else ");
			if (element.condition != null) {
				transpiler.append("if (");
				element.condition.transpile(transpiler);
				transpiler.append(") ");
			}
			transpiler.append("{");
			transpiler.indent();
			element.transpile(transpiler);
			transpiler.dedent();
			transpiler.append("}");
		}
		transpiler.newLine();
		return true;
	}

	public static class IfElement extends CodeSection {

		IExpression condition;
		StatementList statements;

		public IfElement(IExpression condition, StatementList statements) {
			this.condition = condition;
			this.statements = statements;
			this.populateSection();
		}
		
		

		private void populateSection() {
			if(condition instanceof ICodeSection)
				mergeCodeSection(((ICodeSection)condition).getSection());
			if(statements!=null)
				statements.forEach(stmt -> mergeCodeSection(stmt.getSection()));
		}


		public boolean toDialect(CodeWriter writer) {
			switch (writer.getDialect()) {
			case E:
				return toEDialect(writer);
			case O:
				return toODialect(writer);
			case M:
				return toMDialect(writer);
			default:
				return false;
			}
		}

		public boolean toMDialect(CodeWriter writer) {
			return toEDialect(writer);
		}

		public boolean toEDialect(CodeWriter writer) {
			Context context = writer.getContext();
			if (condition != null) {
				writer.append("if ");
				condition.toDialect(writer);
				context = downcastContextForCheck(context);
				if (context != writer.getContext())
					writer = writer.newChildWriter(context);
			}
			writer.append(":\n");
			writer.indent();
			statements.toDialect(writer);
			writer.dedent();
			return false;
		}

		public boolean toODialect(CodeWriter writer) {
			Context context = writer.getContext();
			if (condition != null) {
				writer.append("if (");
				condition.toDialect(writer);
				writer.append(") ");
				context = downcastContextForCheck(context);
				if (context != writer.getContext())
					writer = writer.newChildWriter(context);
			}
			boolean curly = needsCurlyBraces();
			if (curly)
				writer.append("{\n");
			else
				writer.newLine();
			writer.indent();
			statements.toDialect(writer);
			writer.dedent();
			if (curly)
				writer.append("}");
			return curly;
		}
		
		
		private boolean needsCurlyBraces() {
			if(statements == null)
				return false;
			if(statements.size() > 1)
				return true;
			else 
				return !statements.get(0).isSimple();
		}

		public IExpression getCondition() {
			return condition;
		}

		public StatementList getInstructions() {
			return statements;
		}

		public IType check(Context context) {
			if(condition!=null) {
				IType cond = condition.check(context);
				if (cond != BooleanType.instance())
					context.getProblemListener().reportIllegalPredicate(this, condition);
				else
					context = downcastContextForCheck(context);
			}
			return statements.check(context, null);
		}

		public IValue interpret(Context context) throws PromptoError {
			context = downcastForInterpret(context);
			return statements.interpret(context);
		}

		private Context downcastContextForCheck(Context context) {
			Context parent = context;
			if (condition instanceof EqualsExpression)
				context = ((EqualsExpression) condition).downcastForCheck(context);
			context = parent != context ? context : context.newChildContext();
			return context;
		}

		private Context downcastForInterpret(Context context) throws PromptoError {
			Context parent = context;
			if (condition instanceof EqualsExpression)
				context = ((EqualsExpression) condition).downcastForInterpret(context);
			context = parent != context ? context : context.newChildContext();
			return context;
		}

		public void declare(Transpiler transpiler) {
			if (this.condition != null)
				this.condition.declare(transpiler);
			Context context = transpiler.getContext();
			if (this.condition instanceof EqualsExpression)
				context = ((EqualsExpression) condition).downcastForCheck(context);
			if (context != transpiler.getContext())
				transpiler = transpiler.newChildTranspiler(context);
			else
				transpiler = transpiler.newChildTranspiler();
			this.statements.declare(transpiler);
		}

		public boolean transpile(Transpiler transpiler) {
			Context context = transpiler.getContext();
			if (this.condition instanceof EqualsExpression)
				context = ((EqualsExpression) condition).downcastForCheck(context);
			if (context != transpiler.getContext())
				transpiler = transpiler.newChildTranspiler(context);
			else
				transpiler = transpiler.newChildTranspiler();
			this.statements.transpile(transpiler);
			transpiler.flush();
			return false;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy