/* * 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; } }