diff --git a/src/main/java/de/neemann/digital/analyse/TruthTable.java b/src/main/java/de/neemann/digital/analyse/TruthTable.java index 842fe017b..3396403a6 100644 --- a/src/main/java/de/neemann/digital/analyse/TruthTable.java +++ b/src/main/java/de/neemann/digital/analyse/TruthTable.java @@ -61,45 +61,6 @@ public class TruthTable implements Copyable { } } - /** - * Save the table as hex file to be loaded in a ROM or LUT element. - * - * @param filename filename - * @throws IOException IOException - */ - public void saveHex(File filename) throws IOException { - if (results.size() > 63) - throw new IOException(Lang.get("err_tableHasToManyResultColumns")); - - try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8))) { - saveHex(out); - } - } - - /** - * Save the table as hex file to be loaded in a ROM or LUT element. - * - * @param writer the filename to use - * @throws IOException IOException - */ - public void saveHex(Writer writer) throws IOException { - writer.write("v2.0 raw\n"); - int count = results.get(0).getValues().size(); - for (int i = 0; i < count; i++) { - long val = 0; - long mask = 1; - for (Result r : results) { - ThreeStateValue v = r.getValues().get(i); - if (v == ThreeStateValue.one) - val |= mask; - mask *= 2; - } - writer.write(Long.toHexString(val)); - writer.write('\n'); - } - } - - private static XStream getxStream() { XStream xStream = new XStreamValid(); xStream.alias("truthTable", TruthTable.class); diff --git a/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFriday.java b/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterCSV.java similarity index 95% rename from src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFriday.java rename to src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterCSV.java index ba9c4124a..638645049 100644 --- a/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFriday.java +++ b/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterCSV.java @@ -13,7 +13,7 @@ import de.neemann.digital.core.Bits; /** * Exports a table in LogicFriday format */ -public class TruthTableFormatterLogicFriday implements TruthTableFormatter { +public class TruthTableFormatterCSV implements TruthTableFormatter { @Override public String format(TruthTable truthTable) throws ExpressionException { diff --git a/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterHex.java b/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterHex.java new file mode 100644 index 000000000..c7a869ace --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/format/TruthTableFormatterHex.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 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.analyse.format; + +import de.neemann.digital.analyse.TruthTable; +import de.neemann.digital.analyse.expression.ExpressionException; +import de.neemann.digital.analyse.quinemc.ThreeStateValue; + +/** + * Formats a truth table + */ +public class TruthTableFormatterHex implements TruthTableFormatter { + + @Override + public String format(TruthTable truthTable) throws ExpressionException { + StringBuilder sb = new StringBuilder(); + sb.append("v2.0 raw\n"); + + int count = truthTable.getResult(0).size(); + for (int i = 0; i < count; i++) { + long val = 0; + long mask = 1; + for (int j = 0; j < truthTable.getResultCount(); j++) { + ThreeStateValue v = truthTable.getResult(j).get(i); + if (v == ThreeStateValue.one) + val |= mask; + mask *= 2; + } + sb.append(Long.toHexString(val)); + sb.append('\n'); + } + return sb.toString(); + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerLogicFriday.java b/src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensed.java similarity index 67% rename from src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerLogicFriday.java rename to src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensed.java index a123d4ae1..b6ecd6d4c 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerLogicFriday.java +++ b/src/main/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensed.java @@ -12,13 +12,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -public class ExpressionListenerLogicFriday implements ExpressionListener { +public class ExpressionListenerCSVCondensed implements ExpressionListener { private final ArrayList results; private final HashSet names; - private VariableVisitor variables; + private final VariableVisitor variables; private StringBuilder str; - public ExpressionListenerLogicFriday() { + public ExpressionListenerCSVCondensed() { results = new ArrayList<>(); names = new HashSet<>(); variables = new VariableVisitor(); @@ -42,7 +42,6 @@ public class ExpressionListenerLogicFriday implements ExpressionListener { str.append(",").append(r.name); str.append("\n"); - for (Result r : results) r.createString(str, variables.getVariables(), results.size()); } @@ -67,18 +66,43 @@ public class ExpressionListenerLogicFriday implements ExpressionListener { if (expression instanceof Operation.Or) { ArrayList o = ((Operation.Or) expression).getExpressions(); for (Expression e : o) - add(sb, e, variables, results); - } else if (expression instanceof Operation.And) - add(sb, expression, variables, results); - else if (expression instanceof Variable) - add(sb, Operation.and(expression), variables, results); - else if (expression instanceof Not) - add(sb, Operation.and(expression), variables, results); + addPrime(sb, e, variables, results); + } else + addPrime(sb, expression, variables, results); + } + + private void addPrime(StringBuilder sb, Expression and, Collection variables, int results) throws ExpressionException { + if (and instanceof Operation.And) + addAnd(sb, and, variables, results); + else if (and instanceof Variable) + addVar(sb, ((Variable) and).getIdentifier(), variables, results, false); + else if (and instanceof Not && ((Not) and).getExpression() instanceof Variable) + addVar(sb, ((Variable) (((Not) and).getExpression())).getIdentifier(), variables, results, true); else throw new ExpressionException("invalid expression"); } - private void add(StringBuilder sb, Expression and, Collection variables, int results) throws ExpressionException { + private void addVar(StringBuilder sb, String identifier, Collection variables, int results, boolean invert) { + for (Variable var : variables) { + if (var.getIdentifier().endsWith(identifier)) { + if (invert) + sb.append("0,"); + else + sb.append("1,"); + } else + sb.append("X,"); + } + + for (int i = 0; i < results; i++) { + if (i == number) + sb.append(",1"); + else + sb.append(",0"); + } + sb.append('\n'); + } + + private void addAnd(StringBuilder sb, Expression and, Collection variables, int results) throws ExpressionException { HashSet v = new HashSet<>(); HashSet nv = new HashSet<>(); if (and instanceof Operation.And) { @@ -114,6 +138,5 @@ public class ExpressionListenerLogicFriday implements ExpressionListener { } sb.append('\n'); } - } } diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index 03eb7921b..d4687a720 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -16,6 +16,8 @@ import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.expression.format.FormatterException; import de.neemann.digital.analyse.expression.modify.*; import de.neemann.digital.analyse.format.TruthTableFormatter; +import de.neemann.digital.analyse.format.TruthTableFormatterCSV; +import de.neemann.digital.analyse.format.TruthTableFormatterHex; import de.neemann.digital.analyse.format.TruthTableFormatterTestCase; import de.neemann.digital.analyse.quinemc.BoolTableByteArray; import de.neemann.digital.builder.ATF150x.ATFDevice; @@ -439,43 +441,29 @@ public class TableDialog extends JDialog { } }.setToolTip(Lang.get("menu_table_createFunctionFixture_tt")).createJMenuItem()); - fileMenu.add(new ToolTipAction(Lang.get("menu_table_exportHex")) { + JMenu export = new JMenu(Lang.get("menu_export")); + fileMenu.add(export); + export.add(new FileExportAction(Lang.get("menu_table_exportHex"), "hex") { @Override - public void actionPerformed(ActionEvent e) { - int res = JOptionPane.OK_OPTION; - if (undoManager.getActual().getVars().size() > 20) - res = JOptionPane.showConfirmDialog(TableDialog.this, Lang.get("msg_tableHasManyRowsConfirm")); - if (res == JOptionPane.OK_OPTION) { - JFileChooser fc = new MyFileChooser(); - if (TableDialog.this.filename != null) - fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, "hex")); - new SaveAsHelper(TableDialog.this, fc, "hex") - .checkOverwrite(file -> undoManager.getActual().saveHex(file)); - } + protected String getString() throws FormatterException, ExpressionException { + return new TruthTableFormatterHex().format(undoManager.getActual()); } }.setToolTip(Lang.get("menu_table_exportHex_tt")).createJMenuItem()); - - fileMenu.add(new ToolTipAction(Lang.get("menu_table_exportTableLogicFriday")) { + export.add(new FileExportAction(Lang.get("menu_table_exportTableCSVCondensed"), "csv") { @Override - public void actionPerformed(ActionEvent e) { - JFileChooser fc = new MyFileChooser(); - if (TableDialog.this.filename != null) - fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, "csv")); - new SaveAsHelper(TableDialog.this, fc, "csv") - .checkOverwrite(file -> { - ExpressionListenerLogicFriday expressionListener = new ExpressionListenerLogicFriday(); - try { - lastGeneratedExpressions.replayTo(expressionListener); - expressionListener.close(); - try (Writer w = new FileWriter(file)) { - w.write(expressionListener.toString()); - } - } catch (FormatterException | ExpressionException ex) { - throw new IOException(ex); - } - }); + protected String getString() throws FormatterException, ExpressionException { + ExpressionListenerCSVCondensed expressionListener = new ExpressionListenerCSVCondensed(); + lastGeneratedExpressions.replayTo(expressionListener); + expressionListener.close(); + return expressionListener.toString(); } - }.setToolTip(Lang.get("menu_table_exportTableLogicFriday_tt")).createJMenuItem()); + }.setToolTip(Lang.get("menu_table_exportTableCSVCondensed")).createJMenuItem()); + export.add(new FileExportAction(Lang.get("menu_table_exportTableCSV"), "csv") { + @Override + protected String getString() throws FormatterException, ExpressionException { + return new TruthTableFormatterCSV().format(undoManager.getActual()); + } + }.setToolTip(Lang.get("menu_table_exportTableCSV")).createJMenuItem()); createJK = new JCheckBoxMenuItem(Lang.get("menu_table_JK")); createJK.addActionListener(e -> calculateExpressions()); @@ -1021,4 +1009,33 @@ public class TableDialog extends JDialog { abstract ExpressionListener createExpressionListener() throws ExpressionException; } -} + + private abstract class FileExportAction extends ToolTipAction { + private final String suffix; + + private FileExportAction(String name, String suffix) { + super(name); + this.suffix = suffix; + } + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new MyFileChooser(); + if (TableDialog.this.filename != null) + fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, suffix)); + new SaveAsHelper(TableDialog.this, fc, suffix) + .checkOverwrite(file -> { + try { + try (Writer w = new FileWriter(file)) { + w.write(getString()); + } + } catch (FormatterException | ExpressionException ex) { + throw new IOException(ex); + } + }); + } + + protected abstract String getString() throws FormatterException, ExpressionException; + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/analyse/TruthTableTest.java b/src/test/java/de/neemann/digital/analyse/TruthTableTest.java index b844baef1..9026ba3b6 100644 --- a/src/test/java/de/neemann/digital/analyse/TruthTableTest.java +++ b/src/test/java/de/neemann/digital/analyse/TruthTableTest.java @@ -38,52 +38,4 @@ public class TruthTableTest extends TestCase { } } - public void testHexExportSingle() throws Exception { - ArrayList vars = Variable.vars(3); - TruthTable t = new TruthTable(vars).addResult(); - BoolTableByteArray result = (BoolTableByteArray) t.getResult(0); - for (int i = 0; i < t.getRows(); i++) { - result.set(i, i % 2); - } - StringWriter w = new StringWriter(); - t.saveHex(w); - w.close(); - - assertEquals("v2.0 raw\n" + - "0\n" + - "1\n" + - "0\n" + - "1\n" + - "0\n" + - "1\n" + - "0\n" + - "1\n", w.toString()); - } - - public void testHexExportTwo() throws Exception { - ArrayList vars = Variable.vars(3); - TruthTable t = new TruthTable(vars).addResult(); - BoolTableByteArray result = (BoolTableByteArray) t.getResult(0); - for (int i = 0; i < t.getRows(); i++) { - result.set(i, i % 2); - } - t.addResult(); - result = (BoolTableByteArray) t.getResult(1); - for (int i = 0; i < t.getRows(); i++) { - result.set(i, (i + 1) % 2); - } - StringWriter w = new StringWriter(); - t.saveHex(w); - w.close(); - - assertEquals("v2.0 raw\n" + - "2\n" + - "1\n" + - "2\n" + - "1\n" + - "2\n" + - "1\n" + - "2\n" + - "1\n", w.toString()); - } } diff --git a/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterHexTest.java b/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterHexTest.java new file mode 100644 index 000000000..356938537 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterHexTest.java @@ -0,0 +1,59 @@ +package de.neemann.digital.analyse.format; + +import de.neemann.digital.analyse.TruthTable; +import de.neemann.digital.analyse.expression.Variable; +import de.neemann.digital.analyse.quinemc.BoolTableByteArray; +import junit.framework.TestCase; + +import java.io.StringWriter; +import java.util.ArrayList; + +public class TruthTableFormatterHexTest extends TestCase { + + public void testHexExportSingle() throws Exception { + ArrayList vars = Variable.vars(3); + TruthTable t = new TruthTable(vars).addResult(); + BoolTableByteArray result = (BoolTableByteArray) t.getResult(0); + for (int i = 0; i < t.getRows(); i++) { + result.set(i, i % 2); + } + String hex = new TruthTableFormatterHex().format(t); + + assertEquals("v2.0 raw\n" + + "0\n" + + "1\n" + + "0\n" + + "1\n" + + "0\n" + + "1\n" + + "0\n" + + "1\n", hex); + } + + public void testHexExportTwo() throws Exception { + ArrayList vars = Variable.vars(3); + TruthTable t = new TruthTable(vars).addResult(); + BoolTableByteArray result = (BoolTableByteArray) t.getResult(0); + for (int i = 0; i < t.getRows(); i++) { + result.set(i, i % 2); + } + t.addResult(); + result = (BoolTableByteArray) t.getResult(1); + for (int i = 0; i < t.getRows(); i++) { + result.set(i, (i + 1) % 2); + } + + String hex = new TruthTableFormatterHex().format(t); + + assertEquals("v2.0 raw\n" + + "2\n" + + "1\n" + + "2\n" + + "1\n" + + "2\n" + + "1\n" + + "2\n" + + "1\n", hex); + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFridayTest.java b/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicCSV.java similarity index 83% rename from src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFridayTest.java rename to src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicCSV.java index 51a532bec..f89d5c102 100644 --- a/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicFridayTest.java +++ b/src/test/java/de/neemann/digital/analyse/format/TruthTableFormatterLogicCSV.java @@ -5,13 +5,13 @@ import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.quinemc.BoolTableByteArray; import junit.framework.TestCase; -public class TruthTableFormatterLogicFridayTest extends TestCase { +public class TruthTableFormatterLogicCSV extends TestCase { public void testSimple() throws ExpressionException { TruthTable tt = new TruthTable(2); tt.addResult("X", new BoolTableByteArray(new byte[]{0, 1, 1, 1})); tt.addResult("Y", new BoolTableByteArray(new byte[]{0, 0, 0, 1})); - String res = new TruthTableFormatterLogicFriday().format(tt); + String res = new TruthTableFormatterCSV().format(tt); assertEquals("A,B,,X,Y\n" + "0,0,,0,0\n" + diff --git a/src/test/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensedTest.java b/src/test/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensedTest.java new file mode 100644 index 000000000..4e91bf026 --- /dev/null +++ b/src/test/java/de/neemann/digital/gui/components/table/ExpressionListenerCSVCondensedTest.java @@ -0,0 +1,66 @@ +package de.neemann.digital.gui.components.table; + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.ExpressionException; +import de.neemann.digital.analyse.expression.NamedExpression; +import de.neemann.digital.analyse.expression.Variable; +import de.neemann.digital.analyse.expression.format.FormatterException; +import junit.framework.TestCase; + +import static de.neemann.digital.analyse.expression.Not.not; +import static de.neemann.digital.analyse.expression.Operation.and; +import static de.neemann.digital.analyse.expression.Operation.or; + +public class ExpressionListenerCSVCondensedTest extends TestCase { + Variable a = new Variable("A"); + Variable b = new Variable("B"); + Variable c = new Variable("C"); + + public void testSimple() throws FormatterException, ExpressionException { + Expression expression = or(and(a, b), c); + + ExpressionListenerCSVCondensed el = create(expression); + + assertEquals("A,B,C,,Y\n" + + "1,1,X,,1\n" + + "X,X,1,,1\n", el.toString()); + } + + public void testNot() throws FormatterException, ExpressionException { + Expression expression = or(and(a, not(b)), not(c)); + + ExpressionListenerCSVCondensed el = create(expression); + + assertEquals("A,B,C,,Y\n" + + "1,0,X,,1\n" + + "X,X,0,,1\n", el.toString()); + } + + public void testTwo() throws FormatterException, ExpressionException { + Expression e1 = new NamedExpression("Y", or(and(a, b), c)); + Expression e2 = new NamedExpression("X", a); + + ExpressionListenerCSVCondensed el = create(e1, e2); + + assertEquals("A,B,C,,Y,X\n" + + "1,1,X,,1,0\n" + + "X,X,1,,1,0\n" + + "1,X,X,,0,1\n", el.toString()); + } + + private ExpressionListenerCSVCondensed create(Expression... expressions) throws FormatterException, ExpressionException { + ExpressionListenerCSVCondensed el = new ExpressionListenerCSVCondensed(); + + for (Expression e : expressions) { + String name = "Y"; + if (e instanceof NamedExpression) { + NamedExpression ne = (NamedExpression) e; + name = ne.getName(); + e = ne.getExpression(); + } + el.resultFound(name, e); + } + el.close(); + return el; + } +} \ No newline at end of file