results;
private boolean allPassed;
private Exception exception;
@@ -87,55 +90,63 @@ public class TestResult {
model.init();
- for (Value[] rowWithDontCare : lines) {
+ try {
+ lines.emitLines(rowWithDontCare -> {
+ for (Value[] row : resolveDontCares(inputs, rowWithDontCare)) {
- for (Value[] row : resolveDontCares(inputs, rowWithDontCare)) {
+ Value[] res = new Value[row.length];
- Value[] res = new Value[row.length];
-
- boolean clockIsUsed = false;
- // set all values except the clocks
- for (TestSignal in : inputs) {
- if (row[in.index].getType() != Value.Type.CLOCK) {
- row[in.index].copyTo(in.value);
- } else {
- clockIsUsed = true;
- }
- res[in.index] = row[in.index];
- }
-
- if (clockIsUsed) { // a clock signal is used
- model.doStep(); // propagate all except clock
-
- // set clock
- for (TestSignal in : inputs)
- if (row[in.index].getType() == Value.Type.CLOCK)
+ boolean clockIsUsed = false;
+ // set all values except the clocks
+ for (TestSignal in : inputs) {
+ if (row[in.index].getType() != Value.Type.CLOCK) {
row[in.index].copyTo(in.value);
+ } else {
+ clockIsUsed = true;
+ }
+ res[in.index] = row[in.index];
+ }
- // propagate clock change
- model.doStep();
+ try {
+ if (clockIsUsed) { // a clock signal is used
+ model.doStep(); // propagate all except clock
- // restore clock
- for (TestSignal in : inputs) // invert the clock values
- if (row[in.index].getType() == Value.Type.CLOCK)
- in.value.setBool(!in.value.getBool());
- }
+ // set clock
+ for (TestSignal in : inputs)
+ if (row[in.index].getType() == Value.Type.CLOCK)
+ row[in.index].copyTo(in.value);
- try {
- model.doStep();
- } catch (NodeException | RuntimeException e) {
- exception = e;
- allPassed = false;
- return this;
- }
+ // propagate clock change
+ model.doStep();
- for (TestSignal out : outputs) {
- MatchedValue matchedValue = new MatchedValue(row[out.index], out.value);
- res[out.index] = matchedValue;
- if (!matchedValue.isPassed())
+ // restore clock
+ for (TestSignal in : inputs) // invert the clock values
+ if (row[in.index].getType() == Value.Type.CLOCK)
+ in.value.setBool(!in.value.getBool());
+ }
+
+ model.doStep();
+ } catch (NodeException | RuntimeException e) {
+ exception = e;
allPassed = false;
+ throw new RuntimeException(e);
+ }
+
+ for (TestSignal out : outputs) {
+ MatchedValue matchedValue = new MatchedValue(row[out.index], out.value);
+ res[out.index] = matchedValue;
+ if (!matchedValue.isPassed())
+ allPassed = false;
+ }
+ results.add(res);
}
- results.add(res);
+ }, new Context());
+ } catch (ParserException e) {
+ throw new TestingDataException(e);
+ } catch (RuntimeException e) {
+ if (allPassed) {
+ allPassed = false;
+ exception = e;
}
}
diff --git a/src/main/java/de/neemann/digital/testing/Transitions.java b/src/main/java/de/neemann/digital/testing/Transitions.java
index 1337350b9..e6dfa135a 100644
--- a/src/main/java/de/neemann/digital/testing/Transitions.java
+++ b/src/main/java/de/neemann/digital/testing/Transitions.java
@@ -1,6 +1,7 @@
package de.neemann.digital.testing;
import de.neemann.digital.core.element.PinDescription;
+import de.neemann.digital.testing.parser.Context;
import de.neemann.digital.testing.parser.Parser;
import de.neemann.digital.testing.parser.ParserException;
@@ -8,6 +9,7 @@ import java.io.IOException;
import java.util.ArrayList;
/**
+ * Helper to create all possible transitions between states.
* Created by hneemann on 24.02.17.
*/
public class Transitions {
@@ -44,7 +46,7 @@ public class Transitions {
inVarNum.add(i);
}
- for (Value[] line : p.getLines()) {
+ p.getLines().emitLines(line -> {
if (isNormal(line)) {
boolean found = false;
for (Value[] u : uniqueLines) {
@@ -56,7 +58,7 @@ public class Transitions {
if (!found)
uniqueLines.add(line);
}
- }
+ }, new Context());
}
private boolean isInputEqual(Value[] l1, Value[] l2) {
diff --git a/src/main/java/de/neemann/digital/testing/parser/Context.java b/src/main/java/de/neemann/digital/testing/parser/Context.java
index fd098f1dc..684e93092 100644
--- a/src/main/java/de/neemann/digital/testing/parser/Context.java
+++ b/src/main/java/de/neemann/digital/testing/parser/Context.java
@@ -1,93 +1,23 @@
package de.neemann.digital.testing.parser;
import de.neemann.digital.lang.Lang;
-import de.neemann.digital.testing.Value;
-
-import java.util.ArrayList;
-import java.util.HashMap;
/**
* The context of the calculations.
- * Containf the variables to use and the test values list.
+ *
* Created by hneemann on 02.12.16.
*/
public class Context {
- private final HashMap vars;
- private final ArrayList values;
/**
- * Creates a new instance
- */
- public Context() {
- this(null);
- }
-
- /**
- * Creates a new instance
+ * Returnes the value of a variable
*
- * @param values the values array to fill
- */
- public Context(ArrayList values) {
- vars = new HashMap<>();
- this.values = values;
- }
-
- /**
- * @return the actual loop value
- * @throws ParserException Thrown if variable not present
- */
- public long getN() throws ParserException {
- return getVar("n");
- }
-
- /**
- * Returns the value of a variable
- *
- * @param name the variables name
- * @return the long value
- * @throws ParserException Thrown if variable not present
+ * @param name the name of the variable
+ * @return the value
+ * @throws ParserException if the variable does not exist
*/
public long getVar(String name) throws ParserException {
- Long l = vars.get(name);
- if (l == null)
- throw new ParserException(Lang.get("err_variable_N0_notFound", name));
- return l;
+ throw new ParserException(Lang.get("err_variable_N0_notFound", name));
}
- /**
- * Adds a simple value to the value list
- *
- * @param v the value to add
- */
- public void addValue(Value v) {
- values.add(v);
- }
-
- /**
- * Adds bitcount values to the values list.
- * Bitcount bits from the given value are added to the values list
- *
- * @param bitCount the numbers of bits to add
- * @param value the bit values
- */
- public void addBits(int bitCount, long value) {
- long mask = 1L << (bitCount - 1);
- for (int i = 0; i < bitCount; i++) {
- boolean v = (value & mask) != 0;
- values.add(new Value(v ? 1 : 0));
- mask >>= 1;
- }
- }
-
- /**
- * Sets a variable value to the context
- *
- * @param name name of the variable
- * @param value value
- * @return this for chained calls
- */
- public Context setVar(String name, long value) {
- vars.put(name, value);
- return this;
- }
}
diff --git a/src/main/java/de/neemann/digital/testing/parser/ContextWithVar.java b/src/main/java/de/neemann/digital/testing/parser/ContextWithVar.java
new file mode 100644
index 000000000..890ddcdec
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/ContextWithVar.java
@@ -0,0 +1,53 @@
+package de.neemann.digital.testing.parser;
+
+/**
+ * A context with a variable
+ * Created by hneemann on 19.04.17.
+ */
+public class ContextWithVar extends Context {
+
+ private final Context parent;
+ private final String var;
+ private long value;
+
+ /**
+ * Creates a new instance
+ *
+ * @param parent the parent
+ * @param var the variable
+ */
+ public ContextWithVar(Context parent, String var) {
+ this.parent = parent;
+ this.var = var;
+ }
+
+ /**
+ * Creates a new instance
+ *
+ * @param var the variable
+ */
+ public ContextWithVar(String var) {
+ this.parent = new Context();
+ this.var = var;
+ }
+
+ /**
+ * Sets a value to this variable
+ *
+ * @param value the value
+ * @return this for chained calls
+ */
+ public ContextWithVar setValue(long value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public long getVar(String name) throws ParserException {
+ if (name.equals(var))
+ return value;
+ else
+ return parent.getVar(name);
+ }
+
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/LineEmitter.java b/src/main/java/de/neemann/digital/testing/parser/LineEmitter.java
new file mode 100644
index 000000000..98f5b5b42
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/LineEmitter.java
@@ -0,0 +1,18 @@
+package de.neemann.digital.testing.parser;
+
+/**
+ * Used to describe a line
+ * Created by hneemann on 19.04.17.
+ */
+public interface LineEmitter {
+
+ /**
+ * Is called to imit the described line to the listener
+ *
+ * @param listener the listener to emit the lines
+ * @param conext the context
+ * @throws ParserException ParserException
+ */
+ void emitLines(LineListener listener, Context conext) throws ParserException;
+
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/LineEmitterList.java b/src/main/java/de/neemann/digital/testing/parser/LineEmitterList.java
new file mode 100644
index 000000000..432a0b155
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/LineEmitterList.java
@@ -0,0 +1,47 @@
+package de.neemann.digital.testing.parser;
+
+import java.util.ArrayList;
+
+/**
+ * Created by hneemann on 19.04.17.
+ */
+public class LineEmitterList implements LineEmitter {
+
+ private ArrayList lines;
+
+ /**
+ * Create a new instance
+ */
+ public LineEmitterList() {
+ lines = new ArrayList<>();
+ }
+
+ /**
+ * Adds a line to this list
+ *
+ * @param line the line to add
+ */
+ public void add(LineEmitter line) {
+ lines.add(line);
+ }
+
+ /**
+ * If this list contains anly a single line this line is returned.
+ * Otherwise this is returned
+ *
+ * @return this of the only lists item
+ */
+ public LineEmitter minimize() {
+ if (lines.size() == 1)
+ return lines.get(0);
+ else
+ return this;
+ }
+
+ @Override
+ public void emitLines(LineListener listener, Context conext) throws ParserException {
+ for (LineEmitter l : lines)
+ l.emitLines(listener, conext);
+ }
+
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/LineEmitterRepeat.java b/src/main/java/de/neemann/digital/testing/parser/LineEmitterRepeat.java
new file mode 100644
index 000000000..a6b0de1d3
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/LineEmitterRepeat.java
@@ -0,0 +1,34 @@
+package de.neemann.digital.testing.parser;
+
+/**
+ * Repeats some inner table rows.
+ * Created by hneemann on 19.04.17.
+ */
+public class LineEmitterRepeat implements LineEmitter {
+
+ private final String name;
+ private final int size;
+ private final LineEmitter inner;
+
+ /**
+ * Creates a new loop
+ *
+ * @param name name of the loop variable
+ * @param size number of iterations
+ * @param inner the lines to repeat
+ */
+ public LineEmitterRepeat(String name, int size, LineEmitter inner) {
+ this.name = name;
+ this.size = size;
+ this.inner = inner;
+ }
+
+ @Override
+ public void emitLines(LineListener listener, Context conext) throws ParserException {
+ ContextWithVar c = new ContextWithVar(conext, name);
+ for (int i = 0; i < size; i++) {
+ c.setValue(i);
+ inner.emitLines(listener, c);
+ }
+ }
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/LineEmitterSimple.java b/src/main/java/de/neemann/digital/testing/parser/LineEmitterSimple.java
new file mode 100644
index 000000000..a00bc8971
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/LineEmitterSimple.java
@@ -0,0 +1,50 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.lang.Lang;
+import de.neemann.digital.testing.Value;
+
+import java.util.ArrayList;
+
+/**
+ * Line emitter to create a simple row of values.
+ * Created by hneemann on 19.04.17.
+ */
+public class LineEmitterSimple implements LineEmitter {
+
+ private final ArrayList appender;
+ private final int valuesCount;
+ private final int line;
+
+ /**
+ * Creates a new Instance
+ *
+ * @param valuesCount number of expected columns
+ * @param line the source line
+ */
+ public LineEmitterSimple(int valuesCount, int line) {
+ this.valuesCount = valuesCount;
+ this.line = line;
+ this.appender = new ArrayList<>();
+ }
+
+ /**
+ * Adds a value appender
+ *
+ * @param app the appender
+ */
+ public void add(ValueAppender app) {
+ appender.add(app);
+ }
+
+ @Override
+ public void emitLines(LineListener listener, Context conext) throws ParserException {
+ ArrayList vals = new ArrayList<>(valuesCount);
+ for (ValueAppender ve : appender)
+ ve.appendValues(vals, conext);
+
+ if (vals.size() != valuesCount)
+ throw new ParserException(Lang.get("err_testDataExpected_N0_found_N1_numbersInLine_N2", valuesCount, vals.size(), line));
+
+ listener.add(vals.toArray(new Value[vals.size()]));
+ }
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/LineListener.java b/src/main/java/de/neemann/digital/testing/parser/LineListener.java
new file mode 100644
index 000000000..06e7e9345
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/LineListener.java
@@ -0,0 +1,16 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.testing.Value;
+
+/**
+ * Listener for truth table lines
+ * Created by hneemann on 19.04.17.
+ */
+public interface LineListener {
+ /**
+ * Adds a line to the table
+ *
+ * @param values the values in the line
+ */
+ void add(Value[] values);
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/Parser.java b/src/main/java/de/neemann/digital/testing/parser/Parser.java
index 5c282df69..fea0b99a9 100644
--- a/src/main/java/de/neemann/digital/testing/parser/Parser.java
+++ b/src/main/java/de/neemann/digital/testing/parser/Parser.java
@@ -21,9 +21,8 @@ import java.util.ArrayList;
public class Parser {
private final ArrayList names;
- private final ArrayList lines;
- private final ArrayList values;
private final Tokenizer tok;
+ private LineEmitter emitter;
/**
* Creates a new instance
@@ -32,8 +31,6 @@ public class Parser {
*/
public Parser(String data) {
names = new ArrayList<>();
- lines = new ArrayList<>();
- values = new ArrayList<>();
tok = new Tokenizer(new BufferedReader(new StringReader(data)));
}
@@ -46,7 +43,8 @@ public class Parser {
*/
public Parser parse() throws IOException, ParserException {
parseHeader();
- parseValues();
+ emitter = parseValues(false);
+ expect(Tokenizer.Token.EOF);
return this;
}
@@ -71,28 +69,47 @@ public class Parser {
return new ParserException(Lang.get("err_unexpectedToken_N0_inLine_N1", name, tok.getLine()));
}
- private void parseValues() throws IOException, ParserException {
+ private LineEmitter parseValues(boolean loop) throws IOException, ParserException {
+ LineEmitterList list = new LineEmitterList();
while (true) {
Tokenizer.Token t = tok.peek();
switch (t) {
case EOL:
break;
case EOF:
- return;
+ if (loop)
+ throw newUnexpectedToken(t);
+ return list.minimize();
case NUMBER:
- parseLine();
+ list.add(parseLine());
break;
case IDENT:
- if (tok.getIdent().equals("repeat")) {
+ if (tok.getIdent().equals("endloop")) {
+ tok.consume();
+ if (!loop)
+ throw newUnexpectedToken(t);
+ return list.minimize();
+ } else if (tok.getIdent().equals("repeat")) {
tok.consume();
expect(Tokenizer.Token.OPEN);
int count = (int) parseInt();
if (count > 1 << 16)
throw new ParserException(Lang.get("err_toManyTestEntries"));
expect(Tokenizer.Token.CLOSE);
- parseForLine(count);
+ list.add(new LineEmitterRepeat("n", count, parseLine()));
+ } else if (tok.getIdent().equals("loop")) {
+ tok.consume();
+ expect(Tokenizer.Token.OPEN);
+ expect(Tokenizer.Token.IDENT);
+ String var = tok.getIdent();
+ expect(Tokenizer.Token.COMMA);
+ int count = (int) parseInt();
+ if (count > 1 << 16)
+ throw new ParserException(Lang.get("err_toManyTestEntries"));
+ expect(Tokenizer.Token.CLOSE);
+ list.add(new LineEmitterRepeat(var, count, parseValues(true)));
} else {
- parseLine();
+ list.add(parseLine());
}
break;
default:
@@ -101,14 +118,16 @@ public class Parser {
}
}
- private void parseForLine(int count) throws IOException, ParserException {
- ArrayList entries = new ArrayList<>();
+ private LineEmitter parseLine() throws IOException, ParserException {
+ LineEmitterSimple line = null;
while (true) {
Tokenizer.Token token = tok.next();
+ if (line == null)
+ line = new LineEmitterSimple(names.size(), tok.getLine());
switch (token) {
case NUMBER:
Value num = new Value(tok.getIdent());
- entries.add(n -> n.addValue(num));
+ line.add((vals, conext) -> vals.add(num));
break;
case IDENT:
if (tok.getIdent().equals("bits")) {
@@ -116,25 +135,25 @@ public class Parser {
int bitCount = (int) parseInt();
expect(Tokenizer.Token.COMMA);
Expression exp = parseExpression();
- entries.add(c -> c.addBits(bitCount, exp.value(c)));
+ line.add(new ValueAppenderBits(bitCount, exp));
expect(Tokenizer.Token.CLOSE);
} else {
- Value v = new Value(tok.getIdent().toUpperCase());
- entries.add(n -> n.addValue(v));
+ try {
+ final Value value = new Value(tok.getIdent().toUpperCase());
+ line.add((vals, context) -> vals.add(value));
+ } catch (NumberFormatException e) {
+ throw new ParserException(Lang.get("err_notANumber_N0_inLine_N1", tok.getIdent(), tok.getLine()));
+ }
}
break;
case OPEN:
Expression exp = parseExpression();
- entries.add(c -> c.addValue(new Value((int) exp.value(c))));
+ line.add((vals, context) -> vals.add(new Value((int) exp.value(context))));
expect(Tokenizer.Token.CLOSE);
break;
case EOF:
case EOL:
- for (int n = 0; n < count; n++) {
- for (Entry entry : entries) entry.calculate(new Context(values).setVar("n", n));
- addLine();
- }
- return;
+ return line;
default:
throw newUnexpectedToken(token);
}
@@ -145,39 +164,6 @@ public class Parser {
return parseExpression().value(new Context());
}
- private Tokenizer.Token parseLine() throws IOException, ParserException {
- while (true) {
- Tokenizer.Token token = tok.next();
- switch (token) {
- case IDENT:
- case NUMBER:
- try {
- values.add(new Value(tok.getIdent().toUpperCase()));
- } catch (NumberFormatException e) {
- throw new ParserException(Lang.get("err_notANumber_N0_inLine_N1", tok.getIdent(), tok.getLine()));
- }
- break;
- case EOF:
- case EOL:
- addLine();
- return token;
- default:
- throw newUnexpectedToken(token);
- }
- }
- }
-
- private void addLine() throws ParserException {
- if (values.size() > 0) {
-
- if (values.size() != names.size())
- throw new ParserException(Lang.get("err_testDataExpected_N0_found_N1_numbersInLine_N2", names.size(), values.size(), tok.getLine()));
-
- lines.add(values.toArray(new Value[names.size()]));
- values.clear();
- }
- }
-
private void expect(Tokenizer.Token token) throws IOException, ParserException {
Tokenizer.Token t = tok.next();
if (t != token)
@@ -193,14 +179,10 @@ public class Parser {
}
/**
- * @return the test vectors
+ * @return the line emitter
*/
- public ArrayList getLines() {
- return lines;
- }
-
- private interface Entry {
- void calculate(Context c) throws ParserException;
+ public LineEmitter getLines() {
+ return emitter;
}
private boolean isToken(Tokenizer.Token t) throws IOException {
diff --git a/src/main/java/de/neemann/digital/testing/parser/ValueAppender.java b/src/main/java/de/neemann/digital/testing/parser/ValueAppender.java
new file mode 100644
index 000000000..8be832c4e
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/ValueAppender.java
@@ -0,0 +1,20 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.testing.Value;
+
+import java.util.ArrayList;
+
+/**
+ * Used to append some values to a table row
+ * Created by hneemann on 19.04.17.
+ */
+public interface ValueAppender {
+ /**
+ * Appends some values to the given row
+ *
+ * @param values the row
+ * @param context the context to acess variables
+ * @throws ParserException ParserException
+ */
+ void appendValues(ArrayList values, Context context) throws ParserException;
+}
diff --git a/src/main/java/de/neemann/digital/testing/parser/ValueAppenderBits.java b/src/main/java/de/neemann/digital/testing/parser/ValueAppenderBits.java
new file mode 100644
index 000000000..deaa7ed52
--- /dev/null
+++ b/src/main/java/de/neemann/digital/testing/parser/ValueAppenderBits.java
@@ -0,0 +1,36 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.testing.Value;
+
+import java.util.ArrayList;
+
+/**
+ * Appends the bits of an integer value to the given row.
+ * Created by hneemann on 19.04.17.
+ */
+public class ValueAppenderBits implements ValueAppender {
+ private final Expression expression;
+ private final int bitCount;
+
+ /**
+ * Creates a new instance
+ *
+ * @param bitCount the number of bits to append
+ * @param expression the expression to calculate the int value
+ */
+ public ValueAppenderBits(int bitCount, Expression expression) {
+ this.bitCount = bitCount;
+ this.expression = expression;
+ }
+
+ @Override
+ public void appendValues(ArrayList values, Context conext) throws ParserException {
+ long value = expression.value(conext);
+ long mask = 1L << (bitCount - 1);
+ for (int i = 0; i < bitCount; i++) {
+ boolean v = (value & mask) != 0;
+ values.add(new Value(v ? 1 : 0));
+ mask >>= 1;
+ }
+ }
+}
diff --git a/src/test/java/de/neemann/digital/testing/TestDataTest.java b/src/test/java/de/neemann/digital/testing/TestDataTest.java
index d5371bb8c..169d75f8d 100644
--- a/src/test/java/de/neemann/digital/testing/TestDataTest.java
+++ b/src/test/java/de/neemann/digital/testing/TestDataTest.java
@@ -1,5 +1,6 @@
package de.neemann.digital.testing;
+import de.neemann.digital.testing.parser.LineCollector;
import de.neemann.digital.testing.parser.ParserException;
import junit.framework.TestCase;
@@ -15,7 +16,10 @@ public class TestDataTest extends TestCase {
public void testSetDataNonParseable() throws Exception {
TestData td = new TestData(DATA1);
- assertEquals(4, td.getLines().size());
+
+ LineCollector cl = new LineCollector(td.getLines());
+
+ assertEquals(4, cl.getLines().size());
assertEquals(DATA1, td.getDataString());
// try to set a non parsable string
@@ -31,7 +35,10 @@ public class TestDataTest extends TestCase {
public void testSetDataParseable() throws Exception {
TestData td = new TestData(DATA1);
- assertEquals(4, td.getLines().size());
+
+ LineCollector cl = new LineCollector(td.getLines());
+
+ assertEquals(4, cl.getLines().size());
assertEquals(DATA1, td.getDataString());
// try to set a parsable string
diff --git a/src/test/java/de/neemann/digital/testing/parser/LineCollector.java b/src/test/java/de/neemann/digital/testing/parser/LineCollector.java
new file mode 100644
index 000000000..ab76218de
--- /dev/null
+++ b/src/test/java/de/neemann/digital/testing/parser/LineCollector.java
@@ -0,0 +1,38 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.testing.Value;
+
+import java.util.ArrayList;
+
+/**
+ * Created by hneemann on 19.04.17.
+ */
+public class LineCollector implements LineListener {
+ private final ArrayList names;
+ private final ArrayList list;
+
+ public LineCollector(LineEmitter le) throws ParserException {
+ this.list = new ArrayList<>();
+ le.emitLines(this, new Context());
+ names = null;
+ }
+
+ public LineCollector(Parser parser) throws ParserException {
+ this.list = new ArrayList<>();
+ parser.getLines().emitLines(this, new Context());
+ names = parser.getNames();
+ }
+
+ @Override
+ public void add(Value[] values) {
+ list.add(values);
+ }
+
+ public ArrayList getLines() {
+ return list;
+ }
+
+ public ArrayList getNames() {
+ return names;
+ }
+}
diff --git a/src/test/java/de/neemann/digital/testing/parser/ParserExpressionTest.java b/src/test/java/de/neemann/digital/testing/parser/ParserExpressionTest.java
index f13de3c72..0cc05146f 100644
--- a/src/test/java/de/neemann/digital/testing/parser/ParserExpressionTest.java
+++ b/src/test/java/de/neemann/digital/testing/parser/ParserExpressionTest.java
@@ -12,12 +12,12 @@ public class ParserExpressionTest extends TestCase {
public void testParseExpression() throws Exception {
assertEquals(7, new Parser("2+5").getValue());
assertEquals(7, new Parser("9-2").getValue());
- assertEquals(6, new Parser("2*n").getValue(new Context().setVar("n", 3)));
- assertEquals(7, new Parser("2*n+1").getValue(new Context().setVar("n", 3)));
- assertEquals(7, new Parser("1+2*n").getValue(new Context().setVar("n", 3)));
- assertEquals(8, new Parser("2*(1+n)").getValue(new Context().setVar("n", 3)));
- assertEquals(4, new Parser("2*(n-1)").getValue(new Context().setVar("n", 3)));
- assertEquals(2, new Parser("(2*n)/3").getValue(new Context().setVar("n", 3)));
+ assertEquals(6, new Parser("2*n").getValue(new ContextWithVar("n").setValue(3)));
+ assertEquals(7, new Parser("2*n+1").getValue(new ContextWithVar("n").setValue(3)));
+ assertEquals(7, new Parser("1+2*n").getValue(new ContextWithVar("n").setValue(3)));
+ assertEquals(8, new Parser("2*(1+n)").getValue(new ContextWithVar("n").setValue(3)));
+ assertEquals(4, new Parser("2*(n-1)").getValue(new ContextWithVar("n").setValue(3)));
+ assertEquals(2, new Parser("(2*n)/3").getValue(new ContextWithVar("n").setValue(3)));
assertEquals(-1, new Parser("-1").getValue());
assertEquals(-2, new Parser("-1-1").getValue());
@@ -37,10 +37,10 @@ public class ParserExpressionTest extends TestCase {
assertEquals(-1, new Parser("~0").getValue());
- assertEquals(1, new Parser("(n>>8)*(n&255)").getValue(new Context().setVar("n", 257)));
+ assertEquals(1, new Parser("(n>>8)*(n&255)").getValue(new ContextWithVar("n").setValue(257)));
assertEquals(0x11, new Parser("0x10+1").getValue());
- assertEquals(6, new Parser("a*b").getValue(new Context().setVar("a", 2).setVar("b", 3)));
+ assertEquals(6, new Parser("a*b").getValue(new ContextWithVar(new ContextWithVar("a").setValue(2),"b").setValue(3)));
}
public void testVarNotFound() throws IOException {
@@ -54,7 +54,7 @@ public class ParserExpressionTest extends TestCase {
public void testInvalidExpressionClose() throws IOException {
try {
- new Parser("n*3)").getValue(new Context().setVar("n", 2));
+ new Parser("n*3)").getValue(new ContextWithVar("n").setValue(2));
fail();
} catch (ParserException e) {
assertTrue(true);
diff --git a/src/test/java/de/neemann/digital/testing/parser/ParserLoopTest.java b/src/test/java/de/neemann/digital/testing/parser/ParserLoopTest.java
new file mode 100644
index 000000000..ac72cb52e
--- /dev/null
+++ b/src/test/java/de/neemann/digital/testing/parser/ParserLoopTest.java
@@ -0,0 +1,86 @@
+package de.neemann.digital.testing.parser;
+
+import de.neemann.digital.testing.Value;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+/**
+ * Created by hneemann on 19.04.17.
+ */
+public class ParserLoopTest extends TestCase {
+
+ public void testLoop() throws IOException, ParserException {
+ Parser parser = new Parser("A B\nloop(n,10)\n C (n*2)\nendloop").parse();
+ LineCollector td = new LineCollector(parser);
+
+ assertEquals(2, td.getNames().size());
+ assertEquals(10, td.getLines().size());
+
+ for (int i = 0; i < 10; i++) {
+ assertEquals(Value.Type.CLOCK, td.getLines().get(i)[0].getType());
+ assertEquals(i * 2, td.getLines().get(i)[1].getValue());
+ }
+ }
+
+ public void testLoopVar() throws IOException, ParserException {
+ Parser parser = new Parser("A B\nloop(i,10)\n C (i*2)\nendloop").parse();
+ LineCollector td = new LineCollector(parser);
+
+ assertEquals(2, td.getNames().size());
+ assertEquals(10, td.getLines().size());
+
+ for (int i = 0; i < 10; i++) {
+ assertEquals(Value.Type.CLOCK, td.getLines().get(i)[0].getType());
+ assertEquals(i * 2, td.getLines().get(i)[1].getValue());
+ }
+ }
+
+ public void testNested() throws IOException, ParserException {
+ Parser parser = new Parser("A B\nloop(i,10)\nloop(j,10)\n C ((i+j)*2)\nendloop\nendloop").parse();
+ LineCollector td = new LineCollector(parser);
+
+ assertEquals(2, td.getNames().size());
+ assertEquals(100, td.getLines().size());
+
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ int ind = i * 10 + j;
+ assertEquals(Value.Type.CLOCK, td.getLines().get(ind)[0].getType());
+ assertEquals((i + j) * 2, td.getLines().get(ind)[1].getValue());
+ }
+ }
+ }
+
+ public void testLoopMultiLines() throws IOException, ParserException {
+ Parser parser = new Parser("A B\nloop(i,10)\n C (i*2)\n C (i*2+1)\nendloop").parse();
+ LineCollector td = new LineCollector(parser);
+
+ assertEquals(2, td.getNames().size());
+ assertEquals(20, td.getLines().size());
+
+ for (int i = 0; i < 10; i++) {
+ assertEquals(Value.Type.CLOCK, td.getLines().get(i * 2)[0].getType());
+ assertEquals(i * 2, td.getLines().get(i * 2)[1].getValue());
+ assertEquals(Value.Type.CLOCK, td.getLines().get(i * 2 + 1)[0].getType());
+ assertEquals(i * 2 + 1, td.getLines().get(i * 2 + 1)[1].getValue());
+ }
+ }
+
+ public void testMissingEndloop() throws IOException, ParserException {
+ try {
+ new Parser("A B\nloop(i,10) C ((i+j)*2)").parse();
+ fail();
+ } catch (ParserException e) {
+ }
+ }
+
+ public void testUnexpectedEndloop() throws IOException, ParserException {
+ try {
+ new Parser("A B\n C ((i+j)*2)\nendloop").parse();
+ fail();
+ } catch (ParserException e) {
+ }
+ }
+
+}
diff --git a/src/test/java/de/neemann/digital/testing/parser/ParserTest.java b/src/test/java/de/neemann/digital/testing/parser/ParserTest.java
index 2b0aa6c82..7cd30747f 100644
--- a/src/test/java/de/neemann/digital/testing/parser/ParserTest.java
+++ b/src/test/java/de/neemann/digital/testing/parser/ParserTest.java
@@ -13,7 +13,9 @@ import java.io.IOException;
public class ParserTest extends TestCase {
public void testOk() throws TestingDataException, IOException, ParserException {
- Parser td = new Parser("A B\n0 1\n1 0\nX x").parse();
+ Parser parser = new Parser("A B\n0 1\n1 0\nX x").parse();
+ LineCollector td = new LineCollector(parser);
+
assertEquals(2, td.getNames().size());
assertEquals(3, td.getLines().size());
@@ -36,7 +38,8 @@ public class ParserTest extends TestCase {
}
public void testHex() throws TestingDataException, IOException, ParserException {
- Parser td = new Parser("A B\n0 0xff").parse();
+ Parser parser = new Parser("A B\n0 0xff").parse();
+ LineCollector td = new LineCollector(parser);
assertEquals(2, td.getNames().size());
assertEquals(1, td.getLines().size());
@@ -50,7 +53,7 @@ public class ParserTest extends TestCase {
public void testMissingValue() throws IOException, ParserException {
try {
- new Parser("A B\n0 0\n1").parse();
+ new Parser("A B\n0 0\n1").parse().getLines().emitLines(values -> {}, new Context());
assertTrue(false);
} catch (ParserException e) {
assertTrue(true);
@@ -67,7 +70,9 @@ public class ParserTest extends TestCase {
}
public void testClock() throws Exception {
- Parser td = new Parser("A B\nC 1\nC 0").parse();
+ Parser parser = new Parser("A B\nC 1\nC 0").parse();
+ LineCollector td = new LineCollector(parser);
+
assertEquals(2, td.getNames().size());
assertEquals(2, td.getLines().size());
@@ -78,7 +83,9 @@ public class ParserTest extends TestCase {
}
public void testFor() throws IOException, ParserException {
- Parser td = new Parser("A B\nrepeat(10) C (n*2)\n").parse();
+ Parser parser = new Parser("A B\nrepeat(10) C (n*2)\n").parse();
+ LineCollector td = new LineCollector(parser);
+
assertEquals(2, td.getNames().size());
assertEquals(10, td.getLines().size());
@@ -89,7 +96,8 @@ public class ParserTest extends TestCase {
}
public void testForBits() throws IOException, ParserException {
- Parser td = new Parser("A B C D \nrepeat(8) X bits(3,n)\n").parse();
+ Parser parser = new Parser("A B C D \nrepeat(8) X bits(3,n)\n").parse();
+ LineCollector td = new LineCollector(parser);
assertEquals(4, td.getNames().size());
assertEquals(8, td.getLines().size());
@@ -102,19 +110,21 @@ public class ParserTest extends TestCase {
}
public void testComment() throws TestingDataException, IOException, ParserException {
- Parser td = new Parser("#test\nA B\n1 1").parse();
+ Parser parser = new Parser("#test\nA B\n1 1").parse();
+ LineCollector td = new LineCollector(parser);
assertEquals(2, td.getNames().size());
assertEquals(1, td.getLines().size());
}
public void testHeader() throws TestingDataException, IOException, ParserException {
- Parser td = new Parser("A B C D\n1 1 1 1").parse();
+ Parser parser = new Parser("A B C D\n1 1 1 1").parse();
+ LineCollector td = new LineCollector(parser);
assertEquals(4, td.getNames().size());
assertEquals(1, td.getLines().size());
}
public void testEmptyLines() throws TestingDataException, IOException, ParserException {
- Parser td = new Parser("A_i B_i C_i-1 C_i S_i\n" +
+ Parser parser = new Parser("A_i B_i C_i-1 C_i S_i\n" +
" 0 0 0 0 0\n" +
" 0 0 1 0 1\n" +
" 0 1 0 0 1\n\n" +
@@ -123,6 +133,8 @@ public class ParserTest extends TestCase {
" 1 0 1 1 0\n" +
" 1 1 0 1 0\n" +
" 1 1 1 1 1\n").parse();
+ LineCollector td = new LineCollector(parser);
+
assertEquals(5, td.getNames().size());
assertEquals(8, td.getLines().size());
@@ -132,11 +144,11 @@ public class ParserTest extends TestCase {
}
public void testBUG1() throws IOException, ParserException {
- Parser td = new Parser("C_i-1 A B C S\n" +
+ Parser parser = new Parser("C_i-1 A B C S\n" +
"repeat(1<<16) 0 (n>>8) (n&255) ((n>>8)*(n&255)) 0").parse();
+ LineCollector td = new LineCollector(parser);
assertEquals(5, td.getNames().size());
assertEquals(1<<16, td.getLines().size());
-
}
}
\ No newline at end of file