1 /* 2 * Copyright (c) 2019 Helmut Neemann. 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.analyse; 7 8 import de.neemann.digital.core.Bits; 9 import de.neemann.digital.core.element.*; 10 import de.neemann.digital.core.io.InValue; 11 import de.neemann.digital.core.memory.DataField; 12 import de.neemann.digital.draw.elements.Circuit; 13 import de.neemann.digital.draw.elements.PinException; 14 import de.neemann.digital.draw.elements.VisualElement; 15 import de.neemann.digital.draw.library.ElementLibrary; 16 import de.neemann.digital.draw.library.ElementNotFoundException; 17 import de.neemann.digital.draw.library.LibraryInterface; 18 import de.neemann.digital.draw.model.InverterConfig; 19 import de.neemann.digital.draw.shapes.ShapeFactory; 20 import de.neemann.digital.hdl.hgs.*; 21 import de.neemann.digital.lang.Lang; 22 import de.neemann.digital.testing.TestCaseDescription; 23 import org.slf4j.Logger; 24 import org.slf4j.LoggerFactory; 25 26 import java.awt.*; 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.util.ArrayList; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 /** 35 * Used to substitute certain complex builtin components by simple custom components. 36 * Used to allow to analyse this components in a more simple way. 37 */ 38 public class SubstituteLibrary implements LibraryInterface { 39 private static final Logger LOGGER = LoggerFactory.getLogger(SubstituteLibrary.class); 40 private static final Map<String, SubstituteInterface> MAP = new HashMap<>(); 41 42 static { 43 MAP.put("JK_FF", new SubstituteGenericHGSParser("JK_FF.dig")); 44 MAP.put("T_FF", new SubstituteMatching() 45 .add(attr -> attr.get(Keys.WITH_ENABLE), new SubstituteGenericHGSParser("T_FF_EN.dig")) 46 .add(attr -> true, new SubstituteGenericHGSParser("T_FF.dig")) 47 ); 48 MAP.put("Counter", new SubstituteGenericHGSParser("Counter.dig")); 49 MAP.put("CounterPreset", new SubstituteGenericHGSParser("CounterPreset.dig")); 50 MAP.put("Register", new SubstituteGenericHGSParser("Register.dig")); 51 } 52 53 private final ElementLibrary parent; 54 55 /** 56 * Creates a new instance 57 * 58 * @param parent the parent library used to create the not substitutable components. 59 */ SubstituteLibrary(ElementLibrary parent)60 public SubstituteLibrary(ElementLibrary parent) { 61 this.parent = parent; 62 } 63 64 @Override getElementType(String elementName, ElementAttributes attr)65 public ElementTypeDescription getElementType(String elementName, ElementAttributes attr) throws ElementNotFoundException { 66 SubstituteInterface subst = MAP.get(elementName); 67 if (subst != null) { 68 try { 69 ElementTypeDescription type = subst.getElementType(attr, parent); 70 if (type != null) 71 return type; 72 } catch (PinException | IOException e) { 73 throw new ElementNotFoundException(Lang.get("err_substitutingError"), e); 74 } 75 } 76 return parent.getElementType(elementName, attr); 77 } 78 79 @Override getShapeFactory()80 public ShapeFactory getShapeFactory() { 81 return parent.getShapeFactory(); 82 } 83 84 private interface SubstituteInterface { getElementType(ElementAttributes attr, ElementLibrary library)85 ElementTypeDescription getElementType(ElementAttributes attr, ElementLibrary library) throws PinException, IOException; 86 } 87 88 private static final class SubstituteMatching implements SubstituteInterface { 89 private final ArrayList<Matcher> matcher; 90 SubstituteMatching()91 private SubstituteMatching() { 92 matcher = new ArrayList<>(); 93 } 94 add(Accept accept, SubstituteInterface substituteInterface)95 private SubstituteMatching add(Accept accept, SubstituteInterface substituteInterface) { 96 matcher.add(new Matcher(accept, substituteInterface)); 97 return this; 98 } 99 100 @Override getElementType(ElementAttributes attr, ElementLibrary library)101 public ElementTypeDescription getElementType(ElementAttributes attr, ElementLibrary library) throws PinException, IOException { 102 for (Matcher m : matcher) { 103 ElementTypeDescription type = m.getElementType(attr, library); 104 if (type != null) 105 return type; 106 } 107 return null; 108 } 109 } 110 111 private static final class Matcher implements SubstituteInterface { 112 private final Accept accept; 113 private final SubstituteInterface substituteInterface; 114 Matcher(Accept accept, SubstituteInterface substituteInterface)115 private Matcher(Accept accept, SubstituteInterface substituteInterface) { 116 this.accept = accept; 117 this.substituteInterface = substituteInterface; 118 } 119 120 @Override getElementType(ElementAttributes attr, ElementLibrary library)121 public ElementTypeDescription getElementType(ElementAttributes attr, ElementLibrary library) throws PinException, IOException { 122 if (accept.accept(attr)) 123 return substituteInterface.getElementType(attr, library); 124 return null; 125 } 126 } 127 128 private interface Accept { accept(ElementAttributes attr)129 boolean accept(ElementAttributes attr); 130 } 131 132 private static abstract class SubstituteGeneric implements SubstituteInterface { 133 private final String filename; 134 private Circuit circuit; 135 SubstituteGeneric(String filename)136 private SubstituteGeneric(String filename) { 137 this.filename = filename; 138 } 139 140 @Override getElementType(ElementAttributes attr, ElementLibrary library)141 public ElementTypeDescription getElementType(ElementAttributes attr, ElementLibrary library) throws PinException, IOException { 142 if (circuit == null) { 143 LOGGER.debug("load substitute circuit " + filename); 144 InputStream in = getClass().getClassLoader().getResourceAsStream("analyser/" + filename); 145 if (in == null) 146 throw new IOException("substituting failed: could not find file " + filename); 147 148 circuit = Circuit.loadCircuit(in, library.getShapeFactory()); 149 } 150 151 Circuit c = circuit.createDeepCopy(); 152 // disable the normal generic handling! 153 c.getAttributes().set(Keys.IS_GENERIC, false); 154 generify(attr, c); 155 156 return ElementLibrary.createCustomDescription(new File(filename), c, library); 157 } 158 generify(ElementAttributes attr, Circuit circuit)159 private void generify(ElementAttributes attr, Circuit circuit) throws IOException { 160 for (VisualElement v : circuit.getElements()) { 161 String gen = v.getElementAttributes().get(Keys.GENERIC).trim(); 162 if (!gen.isEmpty()) 163 generify(attr, gen, v.getElementAttributes()); 164 } 165 } 166 generify(ElementAttributes sourceAttributes, String gen, ElementAttributes nodeAttributes)167 abstract void generify(ElementAttributes sourceAttributes, String gen, ElementAttributes nodeAttributes) throws IOException; 168 } 169 170 private static final class SubstituteGenericHGSParser extends SubstituteGeneric { 171 private final HashMap<String, Statement> map; 172 SubstituteGenericHGSParser(String filename)173 private SubstituteGenericHGSParser(String filename) { 174 super(filename); 175 map = new HashMap<>(); 176 } 177 178 @Override generify(ElementAttributes sourceAttributes, String gen, ElementAttributes nodeAttributes)179 void generify(ElementAttributes sourceAttributes, String gen, ElementAttributes nodeAttributes) throws IOException { 180 try { 181 Statement s = map.get(gen); 182 if (s == null) { 183 LOGGER.debug("generic: " + gen); 184 s = new Parser(gen).parse(false); 185 map.put(gen, s); 186 } 187 Context context = new Context() 188 .declareVar("orig", sourceAttributes) 189 .declareVar("this", new AllowSetAttributes(nodeAttributes)); 190 s.execute(context); 191 } catch (ParserException | HGSEvalException e) { 192 throw new IOException(e); 193 } 194 } 195 196 } 197 198 /** 199 * Allows writing access to the attributes. 200 */ 201 public static final class AllowSetAttributes implements HGSMap { 202 private final ElementAttributes attr; 203 204 /** 205 * Creates a new instance. 206 * 207 * @param attr the attributes to write to. 208 */ AllowSetAttributes(ElementAttributes attr)209 public AllowSetAttributes(ElementAttributes attr) { 210 this.attr = attr; 211 } 212 213 @Override hgsMapPut(String key, Object val)214 public void hgsMapPut(String key, Object val) throws HGSEvalException { 215 Key k = Keys.getKeyByName(key); 216 if (k == null) { 217 throw new HGSEvalException("key " + key + " is invalid"); 218 } else { 219 Class<?> expectedClass = k.getDefault().getClass(); 220 221 val = doImplicitTypeCasts(expectedClass, val); 222 223 boolean isAssignable = expectedClass.isAssignableFrom(val.getClass()); 224 if (!isAssignable) 225 throw new HGSEvalException("error writing to " + key + ": value of type " + val.getClass().getSimpleName() + " can't be assigned to " + expectedClass.getSimpleName()); 226 attr.set(k, val); 227 } 228 } 229 230 @Override hgsMapGet(String key)231 public Object hgsMapGet(String key) { 232 return attr.hgsMapGet(key); 233 } 234 } 235 doImplicitTypeCasts(Class<?> expectedClass, Object val)236 static Object doImplicitTypeCasts(Class<?> expectedClass, Object val) { 237 if (expectedClass == Integer.class && val instanceof Long) { 238 long l = (Long) val; 239 if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) 240 return (int) l; 241 } else if (expectedClass == Long.class && val instanceof Number) { 242 return ((Number) val).longValue(); 243 } else if (expectedClass == Color.class && val instanceof Number) { 244 return new Color(((Number) val).intValue()); 245 } else if (expectedClass == Boolean.class && val instanceof Number) { 246 long b = ((Number) val).longValue(); 247 return b != 0; 248 } else if (expectedClass == InValue.class) { 249 if (val instanceof Number) 250 return new InValue(((Number) val).longValue()); 251 else { 252 try { 253 return new InValue(val.toString()); 254 } catch (Bits.NumberFormatException e) { 255 return val; 256 } 257 } 258 } else if (expectedClass == InverterConfig.class && val instanceof java.util.List) { 259 InverterConfig.Builder b = new InverterConfig.Builder(); 260 for (Object i : (java.util.List) val) 261 b.add(i.toString()); 262 return b.build(); 263 } else if (expectedClass == DataField.class && val instanceof java.util.List) { 264 java.util.List list = (java.util.List) val; 265 long[] longs = new long[list.size()]; 266 for (int i = 0; i < list.size(); i++) 267 if (list.get(i) instanceof Number) 268 longs[i] = ((Number) list.get(i)).longValue(); 269 else 270 return val; 271 return new DataField(longs); 272 } else if (expectedClass == Rotation.class && val instanceof Number) { 273 int r = ((Number) val).intValue(); 274 return new Rotation(r % 4); 275 } else if (expectedClass == File.class && val instanceof String) { 276 return new File(val.toString()); 277 } else if (expectedClass == TestCaseDescription.class && val instanceof String) { 278 try { 279 return new TestCaseDescription(val.toString()); 280 } catch (Exception e) { 281 return val; 282 } 283 } else if (expectedClass.isEnum() && val instanceof Number) { 284 Class<Enum<?>> e = (Class<Enum<?>>) expectedClass; 285 Object[] values = e.getEnumConstants(); 286 int index = ((Number) val).intValue(); 287 if (index < 0 || index >= values.length) 288 return values[0]; 289 else 290 return values[index]; 291 } 292 return val; 293 } 294 295 } 296