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