From 24df40e67b1c73662009a946390e8bc29b06b322 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 10 Aug 2019 15:37:06 +0200 Subject: [PATCH] added a tt2 renaming of invalid names, see #101 --- .../digital/builder/CleanNameBuilder.java | 111 ++++++++++++++++++ .../digital/builder/tt2/TT2Exporter.java | 7 +- .../digital/builder/CleanNameBuilderTest.java | 80 +++++++++++++ 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/neemann/digital/builder/CleanNameBuilder.java create mode 100644 src/test/java/de/neemann/digital/builder/CleanNameBuilderTest.java diff --git a/src/main/java/de/neemann/digital/builder/CleanNameBuilder.java b/src/main/java/de/neemann/digital/builder/CleanNameBuilder.java new file mode 100644 index 000000000..c8ed5fc79 --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/CleanNameBuilder.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2019 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.builder; + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.Variable; +import de.neemann.digital.analyse.expression.modify.ExpressionModifier; + +import java.util.HashMap; + +/** + * Builder which performs a name cleanup + */ +public class CleanNameBuilder implements BuilderInterface { + + private final BuilderInterface parent; + private final Filter filter; + private final HashMap nameMap; + + /** + * Creates a new instance which allows only characters, numbers and the the underscore. + * + * @param parent the parent builder + */ + public CleanNameBuilder(BuilderInterface parent) { + this(parent, new SimpleFilter()); + } + + /** + * Creates a ne instance + * + * @param parent the parent builder + * @param filter the name filter to use + */ + public CleanNameBuilder(BuilderInterface parent, Filter filter) { + this.parent = parent; + this.filter = filter; + nameMap = new HashMap<>(); + } + + @Override + public CleanNameBuilder addCombinatorial(String name, Expression expression) throws BuilderException { + parent.addCombinatorial(checkName(name), checkName(expression)); + return this; + } + + @Override + public CleanNameBuilder addSequential(String name, Expression expression) throws BuilderException { + parent.addSequential(checkName(name), checkName(expression)); + return this; + } + + private Expression checkName(Expression expression) { + return ExpressionModifier.modifyExpression(expression, exp -> { + if (exp instanceof Variable) { + return new Variable(checkName(((Variable) exp).getIdentifier())); + } else + return exp; + }); + } + + private String checkName(String name) { + String n = nameMap.get(name); + if (n == null) { + n = filter.filter(name); + if (n == null || n.isEmpty()) + n = "X"; + if (nameMap.containsValue(n)) { + int num = 1; + while (nameMap.containsValue(n + num)) + num++; + n = n + num; + } + nameMap.put(name, n); + } + return n; + } + + /** + * Filter interface + */ + public interface Filter { + /** + * Has to return a legal name + * + * @param name the eventually non legal name + * @return the legal name + */ + String filter(String name); + } + + private static final class SimpleFilter implements Filter { + @Override + public String filter(String name) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') + || c == '_') + sb.append(c); + } + + return sb.toString(); + } + } +} diff --git a/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java b/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java index 66141a0d3..ec83d13f1 100644 --- a/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java +++ b/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java @@ -13,6 +13,7 @@ import de.neemann.digital.lang.Lang; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.*; /** @@ -20,6 +21,7 @@ import java.util.*; */ public class TT2Exporter implements ExpressionExporter { private final BuilderCollector builder; + private final CleanNameBuilder cleanNameBuilder; private final PinMap pinMap; private String projectName; private String device; @@ -40,6 +42,7 @@ public class TT2Exporter implements ExpressionExporter { public TT2Exporter(String projectName) { // if simple aliases are filtered out, a direct input to output connection isn't possible anymore builder = new BuilderCollector(); + cleanNameBuilder = new CleanNameBuilder(builder); pinMap = new PinMap().setClockPin(43); device = "f1502ispplcc44"; this.projectName = projectName; @@ -47,7 +50,7 @@ public class TT2Exporter implements ExpressionExporter { @Override public BuilderInterface getBuilder() { - return builder; + return cleanNameBuilder; } /** @@ -68,7 +71,7 @@ public class TT2Exporter implements ExpressionExporter { @Override public void writeTo(OutputStream out) throws FuseMapFillerException, IOException, PinMapException { - writeTo(new OutputStreamWriter(out, "ISO-8859-1")); + writeTo(new OutputStreamWriter(out, StandardCharsets.ISO_8859_1)); } private void writeTo(OutputStreamWriter writer) throws IOException, FuseMapFillerException, PinMapException { diff --git a/src/test/java/de/neemann/digital/builder/CleanNameBuilderTest.java b/src/test/java/de/neemann/digital/builder/CleanNameBuilderTest.java new file mode 100644 index 000000000..278427656 --- /dev/null +++ b/src/test/java/de/neemann/digital/builder/CleanNameBuilderTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Helmut Neemann + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.builder; + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.Operation; +import de.neemann.digital.analyse.expression.Variable; +import junit.framework.TestCase; + +import java.util.Map; + +public class CleanNameBuilderTest extends TestCase { + + public void testSimple() throws BuilderException { + BuilderCollector bc = new BuilderCollector(); + CleanNameBuilder cnb = new CleanNameBuilder(bc); + + String n0 = "z###0"; + String n1 = "z#'#0"; + + cnb.addCombinatorial(n0, Operation.and(new Variable(n0), new Variable(n1))); + cnb.addCombinatorial(n1, Operation.or(new Variable(n0), new Variable(n1))); + + Map comb = bc.getCombinatorial(); + assertEquals(2, comb.size()); + assertEquals("and(z0,z01)", comb.get("z0").toString()); + assertEquals("or(z0,z01)", comb.get("z01").toString()); + } + + public void testSimple2() throws BuilderException { + BuilderCollector bc = new BuilderCollector(); + CleanNameBuilder cnb = new CleanNameBuilder(bc); + + String n0 = "z_0^n"; + String n1 = "z_1^n"; + + cnb.addCombinatorial(n0, Operation.and(new Variable(n0), new Variable(n1))); + cnb.addCombinatorial(n1, Operation.or(new Variable(n0), new Variable(n1))); + + Map comb = bc.getCombinatorial(); + assertEquals(2, comb.size()); + assertEquals("and(z_0n,z_1n)", comb.get("z_0n").toString()); + assertEquals("or(z_0n,z_1n)", comb.get("z_1n").toString()); + } + + public void testEmpty() throws BuilderException { + BuilderCollector bc = new BuilderCollector(); + CleanNameBuilder cnb = new CleanNameBuilder(bc, name -> null); + + String n0 = "z_0^n"; + String n1 = "z_1^n"; + + cnb.addCombinatorial(n0, Operation.and(new Variable(n0), new Variable(n1))); + cnb.addCombinatorial(n1, Operation.or(new Variable(n0), new Variable(n1))); + + Map comb = bc.getCombinatorial(); + assertEquals(2, comb.size()); + assertEquals("and(X,X1)", comb.get("X").toString()); + assertEquals("or(X,X1)", comb.get("X1").toString()); + } + + public void testSequential() throws BuilderException { + BuilderCollector bc = new BuilderCollector(); + CleanNameBuilder cnb = new CleanNameBuilder(bc); + + String n0 = "z_0^n"; + String n1 = "z_1^n"; + + cnb.addSequential(n0 + "+1", Operation.and(new Variable(n0), new Variable(n1))); + cnb.addSequential(n1 + "+1", Operation.or(new Variable(n0), new Variable(n1))); + + Map reg = bc.getRegistered(); + assertEquals(2, reg.size()); + assertEquals("and(z_0n,z_1n)", reg.get("z_0n1").toString()); + assertEquals("or(z_0n,z_1n)", reg.get("z_1n1").toString()); + } +} \ No newline at end of file