
de.adrodoc55.minecraft.mpl.ast.MplAstVisitorImpl Maven / Gradle / Ivy
/*
* Minecraft Programming Language (MPL): A language for easy development of command block
* applications including an IDE.
*
* © Copyright (C) 2016 Adrodoc55
*
* This file is part of MPL.
*
* MPL is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MPL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with MPL. If not, see
* .
*
*
*
* Minecraft Programming Language (MPL): Eine Sprache für die einfache Entwicklung von Commandoblock
* Anwendungen, inklusive einer IDE.
*
* © Copyright (C) 2016 Adrodoc55
*
* Diese Datei ist Teil von MPL.
*
* MPL ist freie Software: Sie können diese unter den Bedingungen der GNU General Public License,
* wie von der Free Software Foundation, Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren
* veröffentlichten Version, weiterverbreiten und/oder modifizieren.
*
* MPL wird in der Hoffnung, dass es nützlich sein wird, aber OHNE JEDE GEWÄHRLEISTUNG,
* bereitgestellt; sogar ohne die implizite Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN
* BESTIMMTEN ZWECK. Siehe die GNU General Public License für weitere Details.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit MPL erhalten haben. Wenn
* nicht, siehe .
*/
package de.adrodoc55.minecraft.mpl.ast;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static de.adrodoc55.minecraft.mpl.ast.chainparts.MplIntercept.INTERCEPTED;
import static de.adrodoc55.minecraft.mpl.ast.chainparts.MplNotify.NOTIFY;
import static de.adrodoc55.minecraft.mpl.commands.Conditional.CONDITIONAL;
import static de.adrodoc55.minecraft.mpl.commands.Conditional.INVERT;
import static de.adrodoc55.minecraft.mpl.commands.Conditional.UNCONDITIONAL;
import static de.adrodoc55.minecraft.mpl.commands.Mode.CHAIN;
import static de.adrodoc55.minecraft.mpl.commands.Mode.IMPULSE;
import static de.adrodoc55.minecraft.mpl.commands.Mode.REPEAT;
import static de.adrodoc55.minecraft.mpl.commands.chainlinks.ReferencingCommand.REF;
import static de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption.DEBUG;
import static de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption.TRANSMITTER;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import com.google.common.annotations.VisibleForTesting;
import de.adrodoc55.minecraft.coordinate.Coordinate3D;
import de.adrodoc55.minecraft.coordinate.Orientation3D;
import de.adrodoc55.minecraft.mpl.ast.chainparts.ChainPart;
import de.adrodoc55.minecraft.mpl.ast.chainparts.Dependable;
import de.adrodoc55.minecraft.mpl.ast.chainparts.ModifiableChainPart;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplBreakpoint;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplCommand;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplIf;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplIntercept;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplNotify;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplStart;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplStop;
import de.adrodoc55.minecraft.mpl.ast.chainparts.MplWaitfor;
import de.adrodoc55.minecraft.mpl.ast.chainparts.loop.MplBreak;
import de.adrodoc55.minecraft.mpl.ast.chainparts.loop.MplBreakLoop;
import de.adrodoc55.minecraft.mpl.ast.chainparts.loop.MplContinue;
import de.adrodoc55.minecraft.mpl.ast.chainparts.loop.MplContinueLoop;
import de.adrodoc55.minecraft.mpl.ast.chainparts.loop.MplWhile;
import de.adrodoc55.minecraft.mpl.ast.chainparts.program.MplProcess;
import de.adrodoc55.minecraft.mpl.ast.chainparts.program.MplProgram;
import de.adrodoc55.minecraft.mpl.chain.ChainContainer;
import de.adrodoc55.minecraft.mpl.chain.CommandChain;
import de.adrodoc55.minecraft.mpl.commands.Conditional;
import de.adrodoc55.minecraft.mpl.commands.Mode;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.ChainLink;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.Command;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.InternalCommand;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.InvertingCommand;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.MplSkip;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.NormalizingCommand;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.ReferencingCommand;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.ReferencingTestforSuccessCommand;
import de.adrodoc55.minecraft.mpl.compilation.CompilerOptions;
import de.adrodoc55.minecraft.mpl.interpretation.IllegalModifierException;
import de.adrodoc55.minecraft.mpl.interpretation.ModifierBuffer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
/**
* @author Adrodoc55
*/
public class MplAstVisitorImpl implements MplAstVisitor {
private ChainContainer container;
@VisibleForTesting
List chains = new ArrayList<>();
@VisibleForTesting
List commands = new ArrayList<>();
@VisibleForTesting
final CompilerOptions options;
private boolean addBreakpointProcess;
public MplAstVisitorImpl(CompilerOptions options) {
this.options = checkNotNull(options, "options == null!");
}
@Override
public ChainContainer getResult() {
return container;
}
/**
* Returns the relative count to the given {@link ChainLink} as a negative integer. If {@code ref}
* is null the returned count will reference the first link in {@link #commands}.
*
* @param ref the {@link ChainLink} to search for
* @return the count to ref
* @throws IllegalArgumentException if {@code ref} is not found
* @throws NullPointerException if {@code ref} is null
*/
private int getCountToRef(ChainLink ref) throws IllegalArgumentException, NullPointerException {
checkNotNull(ref, "ref == null!");
for (int i = commands.size() - 1; i >= 0; i--) {
if (ref == commands.get(i)) {
return -commands.size() + i;
}
}
throw new IllegalArgumentException("The given ref was not found in commands.");
}
public String getStartCommand(String ref) {
if (options.hasOption(TRANSMITTER)) {
return "setblock " + ref + " redstone_block";
} else {
return "blockdata " + ref + " {auto:1b}";
}
}
public String getStopCommand(String ref) {
if (options.hasOption(TRANSMITTER)) {
return "setblock " + ref + " stone";
} else {
return "blockdata " + ref + " {auto:0b}";
}
}
private ReferencingCommand newReferencingStartCommand(boolean conditional, int relative) {
return new ReferencingCommand(getStartCommand(REF), conditional, relative);
}
private ReferencingCommand newReferencingStopCommand(boolean conditional, int relative) {
return new ReferencingCommand(getStopCommand(REF), conditional, relative);
}
private void addRestartBackref(ChainLink chainLink, boolean conditional) {
commands.add(newReferencingStopCommand(conditional, getCountToRef(chainLink)));
commands.add(newReferencingStartCommand(true, getCountToRef(chainLink)));
}
private void addTransmitterReceiverCombo(boolean internal) {
if (options.hasOption(TRANSMITTER)) {
commands.add(new MplSkip(internal));
commands.add(new InternalCommand(getStopCommand("${this - 1}"), Mode.IMPULSE));
} else {
commands.add(new InternalCommand(getStopCommand("~ ~ ~"), Mode.IMPULSE));
}
}
@Override
public void visitProgram(MplProgram program) {
chains = new ArrayList<>(1);
Orientation3D orientation = program.getOrientation();
Coordinate3D max = program.getMax();
CommandChain install = visitUnInstall(program.getInstall());
CommandChain uninstall = visitUnInstall(program.getUninstall());
chains = new ArrayList<>(program.getProcesses().size());
for (MplProcess process : program.getProcesses()) {
process.accept(this);
}
if (addBreakpointProcess) {
addBreakpointProcess(program);
}
container = new ChainContainer(orientation, max, install, uninstall, chains, program.getHash());
}
private CommandChain visitUnInstall(MplProcess process) {
process.accept(this);
CommandChain chain = chains.get(0);
chains.remove(0);
return chain;
}
private void addBreakpointProcess(MplProgram program) {
String hash = program.getHash();
MplProcess process = new MplProcess("breakpoint");
List commands = new ArrayList<>();
// Pause
if (!options.hasOption(TRANSMITTER)) {
commands.add(new MplCommand("/execute @e[tag=" + hash + "] ~ ~ ~ clone ~ ~ ~ ~ ~ ~ ~ ~1 ~"));
}
commands.add(new MplCommand("/tp @e[tag=" + hash + "] ~ ~1 ~"));
if (!options.hasOption(TRANSMITTER)) {
commands
.add(new MplCommand("/execute @e[tag=" + hash + "] ~ ~ ~ blockdata ~ ~ ~ {Command:}"));
}
commands.add(new MplCommand(
"tellraw @a [{\"text\":\"[tp to breakpoint]\",\"color\":\"gold\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/tp @p @e[name=breakpoint_NOTIFY,c=-1]\"}},{\"text\":\" \"},{\"text\":\"[continue program]\",\"color\":\"gold\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/execute @e[name=breakpoint_CONTINUE] ~ ~ ~ "
+ getStartCommand("~ ~ ~") + "\"}}]"));
commands.add(new MplWaitfor("breakpoint_CONTINUE"));
commands.add(new MplCommand("/kill @e[name=breakpoint_CONTINUE]"));
// Unpause
commands.add(
new MplCommand("/execute @e[tag=" + hash + "] ~ ~ ~ clone ~ ~ ~ ~ ~ ~ ~ ~-1 ~ force move"));
commands.add(new MplCommand("/tp @e[tag=" + hash + "] ~ ~-1 ~"));
if (!options.hasOption(TRANSMITTER)) {
commands.add(new MplCommand("/execute @e[tag=" + hash + "] ~ ~ ~ blockdata ~ ~ ~ {Command:"
+ getStopCommand("~ ~ ~") + "}"));
}
commands.add(new MplNotify("breakpoint"));
process.setChainParts(commands);
program.addProcess(process);
process.accept(this);
}
@Override
public void visitProcess(MplProcess process) {
List chainParts = process.getChainParts();
commands = new ArrayList<>(chainParts.size());
boolean containsSkip = containsHighlevelSkip(process);
if (process.getName() != null) {
if (process.isRepeating()) {
if (options.hasOption(TRANSMITTER)) {
commands.add(new MplSkip());
}
if (process.getChainParts().isEmpty()) {
process.add(new MplCommand(""));
}
ChainPart first = chainParts.get(0);
try {
if (containsSkip) {
first.setMode(IMPULSE);
} else {
first.setMode(REPEAT);
}
first.setNeedsRedstone(true);
} catch (IllegalModifierException ex) {
throw new IllegalStateException(ex.getMessage(), ex);
}
} else {
addTransmitterReceiverCombo(false);
}
} else if (options.hasOption(TRANSMITTER)) {
commands.add(new MplSkip());
}
for (ChainPart chainPart : chainParts) {
chainPart.accept(this);
}
if (process.isRepeating() && containsSkip) {
addRestartBackref(commands.get(0), false);
}
chains.add(new CommandChain(process.getName(), commands));
}
private boolean containsHighlevelSkip(MplProcess process) {
List chainParts = process.getChainParts();
for (ChainPart chainPart : chainParts) {
if (chainPart instanceof MplWaitfor//
|| chainPart instanceof MplIntercept//
|| chainPart instanceof MplBreakpoint//
|| chainPart instanceof MplWhile//
|| chainPart instanceof MplBreak//
|| chainPart instanceof MplContinue//
) {
return true;
}
}
return false;
}
/**
* Checks if the given {@link ChainPart} has the {@link Conditional#INVERT INVERT} modifier. If it
* does, an {@link InvertingCommand} is added to {@link #commands}. If {@code chainPart} does not
* have predecessor an {@link IllegalStateException} is thrown.
*
* @param chainPart if {@code chainPart} does not have predecessor
* @throws IllegalStateException
* @see ModifiableChainPart#getPrevious()
*/
protected void visitPossibleInvert(ModifiableChainPart chainPart) throws IllegalStateException {
if (chainPart.getConditional() == Conditional.INVERT) {
Dependable previous = chainPart.getPrevious();
checkState(previous != null,
"Cannot invert ChainPart; no previous command found for " + chainPart);
commands.add(new InvertingCommand(previous.getModeForInverting()));
}
}
@Override
public void visitCommand(MplCommand command) {
visitPossibleInvert(command);
String cmd = command.getCommand();
commands.add(new Command(cmd, command));
}
@Override
public void visitStart(MplStart start) {
visitPossibleInvert(start);
String process = start.getProcess();
String command = "execute @e[name=" + process + "] ~ ~ ~ " + getStartCommand("~ ~ ~");
commands.add(new Command(command, start));
}
@Override
public void visitStop(MplStop stop) {
visitPossibleInvert(stop);
String process = stop.getProcess();
String command = "execute @e[name=" + process + "] ~ ~ ~ " + getStopCommand("~ ~ ~");
commands.add(new Command(command, stop));
}
@Override
public void visitWaitfor(MplWaitfor waitfor) {
ReferencingCommand summon = new ReferencingCommand("summon ArmorStand " + REF + " {CustomName:"
+ waitfor.getEvent() + ",NoGravity:1b,Invisible:1b,Invulnerable:1b,Marker:1b}");
if (waitfor.getConditional() == UNCONDITIONAL) {
summon.setRelative(1);
commands.add(summon);
} else {
summon.setConditional(true);
ReferencingCommand jump = new ReferencingCommand(getStartCommand(REF), true);
if (waitfor.getConditional() == CONDITIONAL) {
summon.setRelative(3);
jump.setRelative(1);
commands.add(summon);
commands.add(new InvertingCommand(CHAIN));
commands.add(jump);
} else { // conditional == INVERT
jump.setRelative(3);
summon.setRelative(1);
commands.add(jump);
commands.add(new InvertingCommand(CHAIN));
commands.add(summon);
}
}
addTransmitterReceiverCombo(false);
}
@Override
public void visitNotify(MplNotify notify) {
visitPossibleInvert(notify);
String process = notify.getProcess();
boolean conditional = notify.isConditional();
commands.add(new InternalCommand(
"execute @e[name=" + process + NOTIFY + "] ~ ~ ~ " + getStartCommand("~ ~ ~"),
conditional));
commands.add(new Command("kill @e[name=" + process + NOTIFY + "]", conditional));
}
@Override
public void visitIntercept(MplIntercept intercept) {
String event = intercept.getEvent();
boolean conditional = intercept.isConditional();
InternalCommand entitydata = new InternalCommand(
"entitydata @e[name=" + event + "] {CustomName:" + event + INTERCEPTED + "}", conditional);
ReferencingCommand summon = new ReferencingCommand("summon ArmorStand " + REF + " {CustomName:"
+ event + ",NoGravity:1b,Invisible:1b,Invulnerable:1b,Marker:1b}", conditional);
if (intercept.getConditional() == UNCONDITIONAL) {
summon.setRelative(1);
commands.add(entitydata);
commands.add(summon);
} else {
ReferencingCommand jump = new ReferencingCommand(getStartCommand(REF), true);
if (intercept.getConditional() == CONDITIONAL) {
summon.setRelative(3);
jump.setRelative(1);
commands.add(entitydata);
commands.add(summon);
commands.add(new InvertingCommand(CHAIN));
commands.add(jump);
} else { // conditional == INVERT
jump.setRelative(4);
summon.setRelative(1);
commands.add(jump);
commands.add(new InvertingCommand(CHAIN));
commands.add(entitydata);
commands.add(summon);
}
}
addTransmitterReceiverCombo(false);
commands.add(new InternalCommand("kill @e[name=" + event + ",r=2]"));
commands.add(new InternalCommand(
"entitydata @e[name=" + event + INTERCEPTED + "] {CustomName:" + event + "}"));
}
@Override
public void visitBreakpoint(MplBreakpoint breakpoint) {
if (!options.hasOption(DEBUG)) {
return;
}
addBreakpointProcess = true;
visitPossibleInvert(breakpoint);
boolean cond = breakpoint.isConditional();
commands.add(new InternalCommand("say " + breakpoint.getMessage(), cond));
ModifierBuffer modifier = new ModifierBuffer();
modifier.setConditional(cond ? CONDITIONAL : UNCONDITIONAL);
visitStart(new MplStart("breakpoint", modifier));
visitWaitfor(new MplWaitfor("breakpoint" + NOTIFY, modifier));
}
@Override
public void visitSkip(MplSkip skip) {
commands.add(skip);
}
@RequiredArgsConstructor
@Getter
@Setter
private static class IfNestingLayer {
private final boolean not;
private final @Nonnull Command ref;
private boolean inElse;
}
private Deque ifNestingLayers = new ArrayDeque<>();
@Override
public void visitIf(MplIf mplIf) {
visitPossibleInvert(mplIf);
String condition = mplIf.getCondition();
Command ref;
if (condition != null) {
ref = new InternalCommand(condition, mplIf);
commands.add(ref);
} else {
ref = (Command) commands.get(commands.size() - 1);
}
if (needsNormalizer(mplIf)) {
ref = new NormalizingCommand();
commands.add(ref);
}
IfNestingLayer layer = new IfNestingLayer(mplIf.isNot(), ref);
ifNestingLayers.push(layer);
// then
layer.setInElse(false);
Deque thenParts = mplIf.getThenParts();
boolean emptyThen = thenParts.isEmpty();
if (!mplIf.isNot() && !emptyThen) {
// First then does not need a reference
addAsConditional(thenParts.pop());
}
addAllWithRef(thenParts);
// else
layer.setInElse(true);
Deque elseParts = mplIf.getElseParts();
boolean emptyElse = elseParts.isEmpty();
if (mplIf.isNot() && emptyThen && !emptyElse) {
// First else does not need a reference, if there is no then part
addAsConditional(elseParts.pop());
}
addAllWithRef(elseParts);
ifNestingLayers.pop();
}
private void addAllWithRef(Iterable chainParts) {
for (ChainPart chainPart : chainParts) {
addWithRef(cast(chainPart));
}
}
private void addWithRef(ModifiableChainPart chainPart) {
visitPossibleInvert(chainPart);
if (chainPart.getConditional() != CONDITIONAL) {
addConditionReferences(chainPart);
}
addAsConditional(chainPart);
}
/**
* Add's all references to required {@link MplIf}s. If the chainPart depends on the parent's
* failure a reference to the grandparent is also added. This method is recursive and will add
* parent references, until the root is reached or until a layer depends on it's parent's success
* rather that failure.
*/
private void addConditionReferences(ModifiableChainPart chainPart) {
Deque requiredReferences = new ArrayDeque<>();
for (IfNestingLayer layer : ifNestingLayers) {
requiredReferences.push(layer);
boolean dependingOnFailure = layer.isNot() ^ layer.isInElse();
if (!dependingOnFailure) {
break;
}
}
if (chainPart.getConditional() == UNCONDITIONAL) {
IfNestingLayer first = requiredReferences.pop();
commands.add(getConditionReference(first));
}
for (IfNestingLayer layer : requiredReferences) {
ReferencingTestforSuccessCommand ref = getConditionReference(layer);
ref.setConditional(true);
commands.add(ref);
}
}
private ReferencingTestforSuccessCommand getConditionReference(IfNestingLayer layer) {
Command ref = layer.getRef();
int relative = getCountToRef(ref);
boolean dependingOnFailure = layer.isNot() ^ layer.isInElse();
return new ReferencingTestforSuccessCommand(relative, ref.getMode(), !dependingOnFailure);
}
private void addAsConditional(ChainPart chainPart) {
cast(chainPart).setConditional(CONDITIONAL);
chainPart.accept(this);
}
private static ModifiableChainPart cast(ChainPart chainPart) {
try {
return (ModifiableChainPart) chainPart;
} catch (ClassCastException ex) {
throw new IllegalStateException("If cannot contain skip", ex);
}
}
public static boolean needsNormalizer(MplIf mplIf) {
if (!mplIf.isNot()) {
return containsConditionReferenceIgnoringFirstNonIf(mplIf.getThenParts());
} else {
if (!mplIf.getThenParts().isEmpty()) {
if (!mplIf.getElseParts().isEmpty())
return true;
else
return false;
}
return containsConditionReferenceIgnoringFirstNonIf(mplIf.getElseParts());
}
}
private static boolean containsConditionReferenceIgnoringFirstNonIf(
Iterable iterable) {
Iterator it = iterable.iterator();
if (it.hasNext()) {
ChainPart first = it.next(); // Ignore the first element.
if (first instanceof MplIf) {
it = iterable.iterator(); // Only if it is not a nested if
}
}
return containsConditionReference(it);
}
private static boolean containsConditionReference(Iterator it) {
while (it.hasNext()) {
ChainPart chainPart = it.next();
if (chainPart instanceof MplIf) {
if (needsParentNormalizer((MplIf) chainPart)) {
return true;
}
} else if (chainPart instanceof ModifiableChainPart) {
ModifiableChainPart cp = (ModifiableChainPart) chainPart;
if (!cp.isConditional()) {
return true;
}
}
}
return false;
}
private static boolean needsParentNormalizer(MplIf mplIf) {
if (mplIf.isNot()) {
return containsConditionReference(mplIf.getThenParts().iterator());
} else {
return containsConditionReference(mplIf.getElseParts().iterator());
}
}
private Deque loops = new ArrayDeque<>();
@Override
public void visitWhile(MplWhile mplWhile) {
loops.push(mplWhile);
String condition = mplWhile.getCondition();
boolean hasInitialCondition = condition != null && !mplWhile.isTrailing();
if (hasInitialCondition) {
visitPossibleInvert(mplWhile);
}
Deque chainParts = mplWhile.getChainParts();
if (chainParts.isEmpty()) {
chainParts.add(new MplCommand(""));
}
int firstIndex = commands.size();
if (hasInitialCondition) {
commands.add(new Command(condition));
}
ReferencingCommand init = new ReferencingCommand(getStartCommand(REF));
ReferencingCommand skip = new ReferencingCommand(getStartCommand(REF), true);
if (!hasInitialCondition && !mplWhile.isConditional()) {
commands.add(init);
} else {
init.setConditional(true);
boolean isNormal = hasInitialCondition && !mplWhile.isNot();
if (isNormal || !hasInitialCondition && mplWhile.getConditional() == CONDITIONAL) {
commands.add(init);
commands.add(new InvertingCommand(CHAIN));
commands.add(skip);
} else {
commands.add(skip);
commands.add(new InvertingCommand(CHAIN));
commands.add(init);
}
}
((Command) commands.get(firstIndex)).setModifier(mplWhile);
// From here the next command will be the entry point for the loop
init.setRelative(-getCountToRef(init));
int entryIndex = commands.size();
if (options.hasOption(TRANSMITTER)) {
commands.add(new MplSkip(true));
}
try {
ChainPart first = chainParts.peek();
if (options.hasOption(TRANSMITTER) && first instanceof MplWhile) {
if (((MplWhile) first).getCondition() == null) {
first = new MplCommand("");
chainParts.push(first);
}
}
first.setMode(IMPULSE);
first.setNeedsRedstone(true);
} catch (IllegalModifierException ex) {
throw new IllegalStateException("while cannot contain skip", ex);
}
boolean dontRestart = false;
for (ChainPart chainPart : chainParts) {
chainPart.accept(this);
if (chainPart instanceof MplBreak || chainPart instanceof MplContinue) {
if (!((ModifiableChainPart) chainPart).isConditional()) {
dontRestart = true;
break;
}
}
}
ChainLink entryPoint = commands.get(entryIndex);
if (!dontRestart) {
if (condition == null) {
addRestartBackref(entryPoint, false);
} else {
commands.add(new Command(condition));
if (!mplWhile.isNot()) {
addContinueLoop(mplWhile).setConditional(true);
commands.add(new InvertingCommand(CHAIN));
addBreakLoop(mplWhile).setConditional(true);
} else {
addBreakLoop(mplWhile).setConditional(true);
commands.add(new InvertingCommand(CHAIN));
addContinueLoop(mplWhile).setConditional(true);
}
}
}
// From here the next command will be the exit point of the loop
int exitIndex = commands.size();
try {
skip.setRelative(-getCountToRef(skip));
} catch (IllegalArgumentException ex) {
// If skip was not added the reference does not have to be set
}
addTransmitterReceiverCombo(true);
ChainLink exitPoint = commands.get(exitIndex);
for (Iterator it = loopRefs.iterator(); it.hasNext();) {
LoopRef loopRef = it.next();
if (loopRef.getLoop() == mplWhile) {
loopRef.setEntryPoint(entryPoint);
loopRef.setExitPoint(exitPoint);
it.remove();
}
}
loops.pop();
}
private List loopRefs = new ArrayList<>();
private interface LoopRef {
MplWhile getLoop();
default void setEntryPoint(ChainLink entryPoint) {}
default void setExitPoint(ChainLink exitPoint) {}
}
public void setRef(ReferencingCommand command, ChainLink reference) {
command.setRelative(getCountToRef(reference) - getCountToRef(command));
}
@Override
public void visitBreak(MplBreak mplBreak) {
MplWhile loop = mplBreak.getLoop();
// FIXME: ein command von break MUSS nicht internal sein (bei unconditional)
Conditional conditional = mplBreak.getConditional();
if (conditional == UNCONDITIONAL) {
addBreakLoop(loop).setModifier(mplBreak);
return;
}
ReferencingCommand dontBreak = new ReferencingCommand(getStartCommand(REF), true);
if (conditional == CONDITIONAL) {
addBreakLoop(loop).setModifier(mplBreak);
commands.add(new InvertingCommand(CHAIN));
commands.add(dontBreak);
} else {
dontBreak.setModifier(mplBreak);
commands.add(dontBreak);
commands.add(new InvertingCommand(CHAIN));
addBreakLoop(loop).setConditional(true);
}
dontBreak.setRelative(-getCountToRef(dontBreak));
addTransmitterReceiverCombo(false);
}
@Override
public void visitContinue(MplContinue mplContinue) {
MplWhile loop = mplContinue.getLoop();
// FIXME: ein command von continue MUSS nicht internal sein (bei unconditional)
Conditional conditional = mplContinue.getConditional();
String condition = loop.getCondition();
if (conditional == UNCONDITIONAL) {
if (condition != null) {
commands.add(new InternalCommand(condition, mplContinue));
addContinueLoop(loop).setConditional(true);
commands.add(new InvertingCommand(CHAIN));
addBreakLoop(loop).setConditional(true);
} else {
addContinueLoop(loop).setModifier(mplContinue);
}
return;
}
MplIf outerIf = new MplIf(false, null);
outerIf.setConditional(mplContinue.isConditional() ? CONDITIONAL : UNCONDITIONAL);
outerIf.setPrevious(mplContinue.getPrevious());
outerIf.enterThen();
if (condition != null) {
MplIf innerIf = new MplIf(false, condition);
innerIf.enterThen();
innerIf.add(new MplContinueLoop(loop));
innerIf.enterElse();
innerIf.add(new MplBreakLoop(loop));
outerIf.add(innerIf);
} else {
outerIf.add(new MplContinueLoop(loop));
}
outerIf.enterElse();
// Mark this command to find it later no user can create such a command
outerIf.add(new MplCommand("//"));
if (conditional == INVERT) {
outerIf.switchThenAndElse();
}
outerIf.accept(this);
for (int i = commands.size() - 1; i >= 0; i--) {
ChainLink chainLink = commands.get(i);
if (chainLink instanceof Command) {
if ("/".equals(((Command) chainLink).getCommand())) {
commands.set(i, new ReferencingCommand(getStartCommand(REF), true, commands.size() - i));
break;
}
}
}
addTransmitterReceiverCombo(false);
}
public void visitBreakLoop(MplBreakLoop mplBreakLoop) {
addBreakLoop(mplBreakLoop.getLoop()).setModifier(mplBreakLoop);
}
private ReferencingCommand addBreakLoop(MplWhile loop) {
ReferencingCommand result = addSkipLoop(loop);
for (MplWhile innerLoop : loops) {
addStopLoop(innerLoop).setConditional(true);
if (innerLoop == loop) {
break;
}
}
return result;
}
public void visitContinueLoop(MplContinueLoop mplContinueLoop) {
addContinueLoop(mplContinueLoop.getLoop()).setModifier(mplContinueLoop);
}
private ReferencingCommand addContinueLoop(MplWhile loop) {
Iterator it = loops.iterator();
MplWhile innerestLoop = it.next();
ReferencingCommand result = addStopLoop(innerestLoop);
if (innerestLoop != loop) {
while (it.hasNext()) {
MplWhile innerLoop = it.next();
addStopLoop(innerLoop).setConditional(true);
if (innerLoop == loop) {
break;
}
}
}
addStartLoop(loop).setConditional(true);
return result;
}
private ReferencingCommand addStartLoop(MplWhile loop) {
ReferencingCommand startLoop = new ReferencingCommand(getStartCommand(REF));
commands.add(startLoop);
loopRefs.add(new LoopRef() {
@Override
public MplWhile getLoop() {
return loop;
}
@Override
public void setEntryPoint(ChainLink entryPoint) {
setRef(startLoop, entryPoint);
}
});
return startLoop;
}
private ReferencingCommand addStopLoop(MplWhile loop) {
ReferencingCommand stopLoop = new ReferencingCommand(getStopCommand(REF));
commands.add(stopLoop);
loopRefs.add(new LoopRef() {
@Override
public MplWhile getLoop() {
return loop;
}
@Override
public void setEntryPoint(ChainLink entryPoint) {
setRef(stopLoop, entryPoint);
}
});
return stopLoop;
}
private ReferencingCommand addSkipLoop(MplWhile loop) {
ReferencingCommand skipLoop = new ReferencingCommand(getStartCommand(REF));
commands.add(skipLoop);
loopRefs.add(new LoopRef() {
@Override
public MplWhile getLoop() {
return loop;
}
@Override
public void setExitPoint(ChainLink exitPoint) {
setRef(skipLoop, exitPoint);
}
});
return skipLoop;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy