/*
* Copyright 2003-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.lang.dataFlow.framework;
import jetbrains.mps.lang.dataFlow.framework.instructions.*;
import jetbrains.mps.logging.Logger;
import org.apache.log4j.LogManager;
import java.util.*;
import java.util.Map.Entry;
public abstract class StructuralProgramBuilder<N> {
private Program myProgram;
protected final List<Runnable> myInvokeLater;
protected final Map<N, Map<String, Integer>> myLabels;
protected final InstructionBuilder instructionBuilder;
private static final Logger LOG = Logger.wrap(LogManager.getLogger(StructuralProgramBuilder.class));
private final ProgramBuilderContext myBuilderContext;
public StructuralProgramBuilder(InstructionBuilder instructionBuilder, ProgramBuilderContext context) {
this.myLabels = new HashMap<N, Map<String, Integer>>();
this.myInvokeLater = new ArrayList<Runnable>();
this.instructionBuilder = instructionBuilder;
myBuilderContext = context;
}
public StructuralProgramBuilder(InstructionBuilder instructionBuilder) {
this(instructionBuilder, new ProgramBuilderContextImpl(Collections.emptyList()));
}
public StructuralProgramBuilder() {
this(new InstructionBuilder());
}
public Program getProgram() {
if (myProgram == null) {
myProgram = createProgram();
}
return myProgram;
}
protected abstract void doBuild(N node);
protected Program createProgram() {
return new Program();
}
public Program buildProgram(N node) {
build(node);
for (Runnable r : myInvokeLater) {
r.run();
}
getProgram().init();
return getProgram();
}
public void build(N node) {
getProgram().start(node);
doBuild(node);
getProgram().end(node);
}
public Position before(final N node) {
return new Position() {
@Override
public int getPosition() {
return getProgram().getStart(node);
}
};
}
public Position after(final N node) {
return new Position() {
@Override
public int getPosition() {
return getProgram().getEnd(node);
}
};
}
public int insertAfter(Instruction i) {
return getProgram().indexOf(i) + 1;
}
public int insertBefore(Instruction i) {
return getProgram().indexOf(i);
}
public Position label(final N node, final String label) {
return new Position() {
@Override
public int getPosition() {
if (!myLabels.containsKey(node) || !myLabels.get(node).containsKey(label)) {
throw new RuntimeException("Can't find a label " + label + " for node " + node);
}
return myLabels.get(node).get(label);
}
};
}
public void emitLabel(String label) {
if (!myLabels.containsKey(getProgram().getCurrent())) {
myLabels.put((N) getProgram().getCurrent(), new HashMap<String, Integer>());
}
myLabels.get(getProgram().getCurrent()).put(label, getProgram().size());
}
protected void updateLabelsOnInsert(final int position) {
for (Entry<N, Map<String, Integer>> labels : myLabels.entrySet()) {
for (Entry<String, Integer> label : labels.getValue().entrySet()) {
if (label.getValue() > position) {
label.setValue(label.getValue() + 1);
}
}
}
}
public void insertInstruction(Instruction instruction, int position) {
updateLabelsOnInsert(position);
getProgram().insert(instruction, position, false, false);
}
protected NopInstruction emitNopCommon() {
return emitNopCommon(null);
}
protected NopInstruction emitNopCommon(String ruleNodeReference) {
NopInstruction instruction = instructionBuilder.createNopInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
return instruction;
}
public void emitNop(final int insertPosition) {
insertInstruction(emitNopCommon(null), insertPosition);
}
public void emitNop(final int insertPosition, String ruleNodeReference) {
insertInstruction(emitNopCommon(ruleNodeReference), insertPosition);
}
public void emitNop() {
getProgram().add(emitNopCommon(null));
}
public void emitNop(String ruleNodeReference) {
getProgram().add(emitNopCommon(ruleNodeReference));
}
public void emitRead(Object var, String ruleNodeReference) {
ReadInstruction instruction = instructionBuilder.createReadInstruction(ruleNodeReference, var);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitRead(Object var) {
emitRead(var, null);
}
public void emitWrite(Object var, Object value, String ruleNodeReference) {
WriteInstruction instruction = instructionBuilder.createWriteInstruction(ruleNodeReference, var, value);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitWrite(Object var, Object value) {
emitWrite(var, value, null);
}
public void emitWrite(Object var, String ruleNodeReference) {
emitWrite(var, null, ruleNodeReference);
}
public void emitWrite(Object var) {
emitWrite(var, null, null);
}
public void emitRet(String ruleNodeReference) {
RetInstruction instruction = instructionBuilder.createRetInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitRet() {
emitRet(null);
}
public void emitJump(final Position position, String ruleNodeReference) {
final JumpInstruction instruction = instructionBuilder.createJumpInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
getProgram().add(instruction);
invokeLater(new Runnable() {
@Override
public void run() {
try {
instruction.setJumpTo(position);
} catch (DataflowBuilderException e) {
LOG.warning("JumpTo instruction reference to outer node");
instruction.getProgram().setHasOuterJumps(true);
}
}
});
}
public void emitJump(final Position position) {
emitJump(position, null);
}
protected IfJumpInstruction emitIfJumpCommon(final Position position, String ruleNodeReference) {
final IfJumpInstruction instruction = instructionBuilder.createIfJumpInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
invokeLater(new Runnable() {
@Override
public void run() {
try {
instruction.setJumpTo(position);
} catch (DataflowBuilderException e) {
LOG.warning("IfJumpTo instruction reference to outer node");
instruction.getProgram().setHasOuterJumps(true);
}
}
});
return instruction;
}
public void emitIfJump(final Position position, String ruleNodeReference) {
getProgram().add(emitIfJumpCommon(position, ruleNodeReference));
}
public void emitIfJump(final Position position) {
getProgram().add(emitIfJumpCommon(position, null));
}
public void emitIfJump(final Position position, int insertPosition, String ruleNodeReference) {
insertInstruction(emitIfJumpCommon(position, ruleNodeReference), insertPosition);
}
public void emitIfJump(final Position position, int insertPosition) {
insertInstruction(emitIfJumpCommon(position, null), insertPosition);
}
public void emitTry(String ruleNodeReference) {
TryInstruction instruction = instructionBuilder.createTryInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitTry() {
emitTry(null);
}
public void emitFinally(String ruleNodeReference) {
FinallyInstruction instruction = instructionBuilder.createFinallyInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitFinally() {
emitFinally(null);
}
public void emitEndTry(String ruleNodeReference) {
EndTryInstruction instruction = instructionBuilder.createEndTryInstruction(ruleNodeReference);
onInstructionEmitted(instruction);
getProgram().add(instruction);
}
public void emitEndTry() {
emitEndTry(null);
}
public void addInstruction(Instruction instruction, Position position) {
if (position == null) {
getProgram().add(instruction);
} else {
insertInstruction(instruction, position.getPosition());
}
}
protected void onInstructionEmitted(Instruction instruction) {
}
protected void invokeLater(Runnable r) {
myInvokeLater.add(r);
}
public interface Position {
int getPosition();
}
public boolean contains(Object o) {
return getProgram().contains(o);
}
public List<Instruction> getInstructionsFor(Object o) {
return getProgram().getInstructionsFor(o);
}
protected ProgramBuilderContext getBuilderContext() {
return myBuilderContext;
}
}