mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-23 04:11:54 -04:00
first working CSV export, see #631
This commit is contained in:
parent
96a2858d85
commit
1a25ac7279
@ -61,45 +61,6 @@ public class TruthTable implements Copyable<TruthTable> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -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 {
|
@ -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();
|
||||
}
|
||||
}
|
@ -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<Result> results;
|
||||
private final HashSet<String> 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<Expression> 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<Variable> 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<Variable> variables, int results) throws ExpressionException {
|
||||
private void addVar(StringBuilder sb, String identifier, Collection<Variable> 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<Variable> variables, int results) throws ExpressionException {
|
||||
HashSet<String> v = new HashSet<>();
|
||||
HashSet<String> nv = new HashSet<>();
|
||||
if (and instanceof Operation.And) {
|
||||
@ -114,6 +138,5 @@ public class ExpressionListenerLogicFriday implements ExpressionListener {
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -38,52 +38,4 @@ public class TruthTableTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testHexExportSingle() throws Exception {
|
||||
ArrayList<Variable> 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<Variable> 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());
|
||||
}
|
||||
}
|
||||
|
@ -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<Variable> 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<Variable> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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" +
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user