diff --git a/build.xml b/build.xml
index 79a23b0..23e00a7 100755
--- a/build.xml
+++ b/build.xml
@@ -48,6 +48,7 @@
+
@@ -293,7 +294,7 @@
-
+
diff --git a/etc/sh/bench b/etc/sh/bench
index 005b25c..11e8262 100755
--- a/etc/sh/bench
+++ b/etc/sh/bench
@@ -15,6 +15,15 @@ jrte=$(realpath $(dirname $0)/../..)
jars=$jrte/jars
version=$(cat $jrte/version)
path="$jars/$version.jar:$jars/$version-test.jar"
+vmargs=""
+while [[ ! "$1" =~ ^(--tsv|[0-9]+) ]]; do
+ vmargs="$vmargs $1";
+ shift
+ if (($#==0)); then
+ execHelp
+ exit 0
+ fi
+done
format=cat
if [[ "$1" == "--tsv" ]]; then
format=strip
@@ -32,5 +41,5 @@ input=$4
shift 4
output="-Djrte.out.enabled=false -Dregex.out.enabled=false"
for i in $(seq $n); do
- ${JAVA_HOME}/bin/java -Dfile.encoding=UTF-8 $output -cp "$path" com.characterforming.jrte.test.FileRunner --nil "$model" $transducer "$input" "$@"|$format
+ ${JAVA_HOME}/bin/java $vmargs -Dfile.encoding=UTF-8 $output -cp "$path" com.characterforming.jrte.test.FileRunner --nil "$model" $transducer "$input" "$@"|$format
done
diff --git a/patterns/ribose/Automaton.inr b/patterns/ribose/Automaton.inr
index a0cb5bf..33e1e24 100755
--- a/patterns/ribose/Automaton.inr
+++ b/patterns/ribose/Automaton.inr
@@ -1,6 +1,6 @@
Sign = ('-', paste)?;
Number = (digit, paste)+;
-Symbol = [count[`~length` `!eol`]] (byte, paste count)* eol;
+Symbol = (byte, paste count)* eol;
Field = select[X] clear;
Eol = cr? nl;
Inr = 'INR';
@@ -17,7 +17,7 @@ Automaton = nil? (
(tab, Field@(X,`~to`)) Number
(tab, Field@(X,`~tape`)) Sign? Number
(tab, Field@(X,`~length`)) Number
- (tab, Field@(X,`~symbol`)) Symbol
+ (tab, (Field@(X,`~symbol`)) count[`~length` `!eol`]) Symbol
(Eol, transition (Field@(X,`~from`)))
)*
(eos, automaton stop)
diff --git a/src/com/characterforming/jrte/engine/ModelCompiler.java b/src/com/characterforming/jrte/engine/ModelCompiler.java
index 93f59b5..02c2535 100755
--- a/src/com/characterforming/jrte/engine/ModelCompiler.java
+++ b/src/com/characterforming/jrte/engine/ModelCompiler.java
@@ -61,7 +61,10 @@
public final class ModelCompiler extends Model implements ITarget, AutoCloseable {
private static final long VERSION = 210;
+ private static final String AUTOMATON = "Automaton";
private static final String AMBIGUOUS_STATE_MESSAGE = "%1$s: Ambiguous state %2$d";
+ private static final int MIN_PRODUCT_LENGTH = Integer.parseInt(System.getProperty("ribose.product.threshold", "12"));
+ private static final int MIN_SUM_SIZE = Integer.parseInt(System.getProperty("ribose.sum.threshold", "128"));
private Bytes transducerName;
private int transducerOrdinal;
@@ -78,7 +81,6 @@ public final class ModelCompiler extends Model implements ITarget, AutoCloseable
private int[][][] kernelMatrix;
private int transition;
- private static final String AUTOMATON = "Automaton";
record Transition (int from, int to, int tape, Bytes symbol, boolean isFinal) {}
@@ -683,7 +685,7 @@ private void factor(final int[][][] transitionMatrix) throws CharacterCodingExce
break;
}
}
- } else if (selfCount > 64) {
+ } else if (selfCount > ModelCompiler.MIN_SUM_SIZE) {
assert msumStateEffects[state] == null;
Argument argument = new Argument(-1, new BytesArray(new byte[][] { Arrays.copyOf(selfBytes, selfCount) }));
int msumParameterIndex = super.compileParameters(msumOrdinal, argument);
@@ -748,7 +750,7 @@ private void factor(final int[][][] transitionMatrix) throws CharacterCodingExce
assert inputEquivalenceToken[exitEquivalent[toState]] >= 0;
assert inputEquivalenceToken[exitEquivalent[toState]] < nulSignal;
int nextState = this.walk(toState, walkedBytes, walkResult, exitEquivalent, inputEquivalenceToken);
- if (walkResult[0] > 3) {
+ if (walkResult[0] > ModelCompiler.MIN_PRODUCT_LENGTH) {
assert this.inputEquivalenceIndex[walkedBytes[0]] == exitEquivalent[toState];
if (mproductStateEffects[toState] == null) {
byte[][] product = new byte[][] { Arrays.copyOfRange(walkedBytes, 0, walkResult[0]) };
diff --git a/src/com/characterforming/jrte/engine/Transductor.java b/src/com/characterforming/jrte/engine/Transductor.java
index c799adf..28803bf 100644
--- a/src/com/characterforming/jrte/engine/Transductor.java
+++ b/src/com/characterforming/jrte/engine/Transductor.java
@@ -249,7 +249,7 @@ public long asInteger(int fieldOrdinal) throws EffectorException {
n = (10 * n) + (digit - 48);
} else {
throw new NumberFormatException(String.format(
- "Not a numeric value '%1$s'", this.toString()));
+ "Not a numeric value '%1$s'", this.toString()));
}
}
return sign * n;
@@ -276,7 +276,7 @@ public double asReal(int fieldOrdinal) throws EffectorException {
mark = true;
} else {
throw new NumberFormatException(String.format(
- "Not a floating point value '%1$s'", this.toString()));
+ "Not a floating point value '%1$s'", this.toString()));
}
}
return f * n;
@@ -328,9 +328,8 @@ public Status status() {
} else {
return this.inputStack.isEmpty() ? Status.PAUSED : Status.RUNNABLE;
}
- } else {
- return Status.PROXY;
}
+ return Status.PROXY;
}
@Override // @see com.characterforming.ribose.ITransductor#output(OutputStream)
@@ -398,6 +397,7 @@ public ITransductor run() throws EffectorException, DomainErrorException {
int token = -1, state = 0, last = -1, signal = 0;
if (this.prologue != null) {
signal = this.prologue.signal();
+ this.matchMode = MATCH_NONE;
this.prologue = null;
}
Input input = Input.empty;
@@ -412,8 +412,10 @@ public ITransductor run() throws EffectorException, DomainErrorException {
this.selected = this.transducer.selected;
state = this.transducer.state;
I: do {
+ int pos = input.position;
// get next input token
if (signal > 0) {
+ pos = input.position;
token = signal;
signal = 0;
} else {
@@ -424,49 +426,60 @@ public ITransductor run() throws EffectorException, DomainErrorException {
break T;
}
}
- token = 0xff & input.array[input.position++];
+ pos = input.position++;
+ token = 0xff & input.array[pos];
}
- // absorb self-referencing (msum,mscan) or sequential (mproduct) transitions with nil effect
- while (this.matchMode != MATCH_NONE && token < SIGNUL) {
+ int action = NIL, mode = this.matchMode;
+S: do {
switch (this.matchMode) {
+ // trap runs in (nil* paste*)* effector space
+ case MATCH_NONE:
+ do {
+ long transition = transitionMatrix[state + inputFilter[token]];
+ last = state; state = Transducer.state(transition);
+ action = Transducer.action(transition);
+ if (action == PASTE)
+ this.value.paste((byte)token);
+ else if (action != NIL)
+ break S;
+ token = input.position < input.limit
+ ? 0xff & input.array[input.position++]
+ : -1;
+ } while (token >= 0);
+ break;
+ // absorb self-referencing (msum,mscan) or sequential (mproduct) transitions with nil effect
case MATCH_SUM:
- token = sumTrap(input, token);
+ if (token < SIGNUL)
+ token = sumTrap(input, token);
break;
case MATCH_PRODUCT:
- token = productTrap(input, token);
+ if (token < SIGNUL)
+ token = productTrap(input, token);
break;
case MATCH_SCAN:
- token = scanTrap(input, token);
+ if (token < SIGNUL)
+ token = scanTrap(input, token);
break;
default:
assert false;
break;
}
if (token < 0) {
- continue I;
- }
- }
-
- // trap runs in (nil* paste*)* effector space
- int action;
-S: do {
- long transition = transitionMatrix[state + inputFilter[token]];
- last = state; state = Transducer.state(transition);
- action = Transducer.action(transition);
- if (action == PASTE) {
- this.value.paste((byte)token);
- } else if (action != NIL) {
- break S;
- }
- if (input.position < input.limit) {
- token = 0xff & input.array[input.position++];
- } else {
+ if (input.position > pos) {
+ this.metrics.traps[mode][0] += input.position > pos ? 1 : 0;
+ this.metrics.traps[mode][1] += input.position - pos;
+ }
continue I;
}
} while (true);
+ if (input.position > pos) {
+ this.metrics.traps[mode][0] += input.position > pos ? 1 : 0;
+ this.metrics.traps[mode][1] += input.position - pos;
+ }
// effect action and check for transducer or input stack adjustment
+ assert this.matchMode == MATCH_NONE;
int aftereffects = effect(action, token, effectorVector);
if (aftereffects != IEffector.RTX_NONE) {
if (0 != (aftereffects & IEffector.RTX_INPUT)) {
@@ -624,17 +637,14 @@ private int effect(int action, int token, int[] effectorVector)
}
private int sumTrap(Input input, int token) {
- final int anchor = input.position;
final long[] matchMask = this.matchSum;
while (0 != (matchMask[token >> 6] & (1L << (token & 0x3f)))) {
if (input.position < input.limit) {
token = 0xff & input.array[input.position++];
} else {
- this.metrics.sum += (input.position - anchor);
return -1;
}
}
- this.metrics.sum += (input.position - anchor);
this.matchMode = MATCH_NONE;
return token;
}
@@ -656,28 +666,23 @@ private int productTrap(Input input, int token) {
}
} else {
this.errorInput = 0xff & match;
- this.metrics.product += mpos;
this.matchMode = MATCH_NONE;
return SIGNUL;
}
}
- this.metrics.product += mpos;
this.matchMode = MATCH_NONE;
return 0xff & match;
}
private int scanTrap(Input input, int token) {
- final int anchor = input.position;
final int matchToken = this.matchByte;
while (token != matchToken) {
if (input.position < input.limit) {
token = 0xff & input.array[input.position++];
} else {
- this.metrics.scan += (input.position - anchor);
return -1;
}
}
- this.metrics.scan += (input.position - anchor);
this.matchMode = MATCH_NONE;
return token;
}
@@ -685,7 +690,7 @@ private int scanTrap(Input input, int token) {
private String getErrorInput(int last, int state) {
TransducerState top = this.transducerStack.peek();
int eqCount = top.get().getInputEquivalentsCount();
- state /= eqCount; last /= eqCount;
+ top.state = state; state /= eqCount; last /= eqCount;
StringBuilder message = new StringBuilder(256);
message.append(String.format("Domain error on (%1$d~%2$d) in %3$s [%4$d]->[%5$d]%n,\tTransducer stack:%n",
this.errorInput, this.errorInput >= 0 ? top.get().getInputFilter()[this.errorInput] : this.errorInput,
@@ -900,7 +905,7 @@ public Integer[] allocateParameters(int parameterCount) {
@Override
public Integer compileParameter(final IToken[] parameterList) throws TargetBindingException {
if (parameterList.length != 1) {
- throw new TargetBindingException("The signal effector accepts at most one parameter");
+ throw new TargetBindingException("The signal effector accepts exactly one parameter");
} else if (parameterList[0] instanceof Token token) {
if (token.getType() == IToken.Type.SIGNAL) {
int ordinal = token.getOrdinal();
@@ -922,7 +927,7 @@ public Integer compileParameter(final IToken[] parameterList) throws TargetBindi
}
} else {
throw new TargetBindingException(String.format("Unknown IToken implementation class '%1$s'",
- parameterList[0].getClass().getTypeName()));
+ parameterList[0].getClass().getTypeName()));
}
}
}
diff --git a/src/com/characterforming/jrte/test/FileRunner.java b/src/com/characterforming/jrte/test/FileRunner.java
index e599f08..c47517d 100644
--- a/src/com/characterforming/jrte/test/FileRunner.java
+++ b/src/com/characterforming/jrte/test/FileRunner.java
@@ -117,7 +117,6 @@ public static void main(final String[] args) {
}
t1 = System.nanoTime() - t0;
if (i >= 10) {
- System.out.print(String.format("%4d", t1/(1000000)));
tjrte += t1;
}
assert !trex.status().isRunnable();
@@ -125,12 +124,23 @@ public static void main(final String[] args) {
assert trex.status().isStopped();
}
}
- double mbps = (tjrte > 0) ? (double)(metrics.bytes*1000000000l) / (double)(tjrte*1024*1024) : -1;
- double mps = (metrics.bytes > 0) ? ((double)(100 * metrics.sum) / (double)metrics.bytes) : -1;
- double mpr = (metrics.bytes > 0) ? ((double)(100 * metrics.product) / (double)metrics.bytes) : -1;
- double msc = (metrics.bytes > 0) ? ((double)(100 * metrics.scan) / (double)metrics.bytes) : -1;
- double ekb = (metrics.bytes > 0) ? ((double)(1024 * metrics.errors) / (double)metrics.bytes) : -1;
- System.out.println(String.format(" : %8.3f mb/s %7.3f nul/kb %6.3f%% m+ %6.3f%% m* %6.3f%% m-", mbps, ekb, mps, mpr, msc));
+ long bytes = metrics.traps[0][1] + metrics.traps[1][1] + metrics.traps[2][1] + metrics.traps[3][1];
+ double mbps = (tjrte > 0) ? (double)(bytes*1000000000l) / (double)(tjrte*1024*1024) : -1;
+ double mnone = (bytes > 0) ? ((double) (100 * metrics.traps[0][1]) / (double) bytes) : -1;
+ double mps = (bytes > 0) ? ((double) (100 * metrics.traps[1][1]) / (double) bytes) : -1;
+ double mpr = (bytes > 0) ? ((double)(100 * metrics.traps[2][1]) / (double)bytes) : -1;
+ double msc = (bytes > 0) ? ((double)(100 * metrics.traps[3][1]) / (double)bytes) : -1;
+ double ekb = (bytes > 0) ? ((double)(1024 * metrics.errors) / (double)bytes) : -1;
+ long none = metrics.traps[0][0] > 0 ? metrics.traps[0][1] / metrics.traps[0][0] : 0;
+ long sum = metrics.traps[1][0] > 0 ? metrics.traps[1][1] / metrics.traps[1][0] : 0;
+ long product = metrics.traps[2][0] > 0 ? metrics.traps[2][1] / metrics.traps[2][0] : 0;
+ long scan = metrics.traps[3][0] > 0 ? metrics.traps[3][1] / metrics.traps[3][0] : 0;
+ String snone= String.format("(%d/%.2f%%):none", none, mnone);
+ String ssum = String.format("(%d/%.2f%%):msum", sum, mps);
+ String sproduct = String.format("(%d/%.2f%%):mproduct", product, mpr);
+ String sscan = String.format("(%d/%.2f%%):mscan", scan, msc);
+ System.out.println(String.format("%8.3f mb/s %7.3f nul/kb %16s %16s %20s %17s",
+ mbps, ekb, snone, ssum, sproduct, sscan));
} else {
try (
final FileInputStream isr = new FileInputStream(f);
@@ -172,13 +182,12 @@ public static void main(final String[] args) {
}
t1 = System.nanoTime() - t0;
if (i >= 10) {
- System.out.print(String.format("%4d", count > 0 ? t1/1000000 : -1));
tregex += t1;
}
assert count > 0;
}
double mbps = (tregex > 0) ? (double)((loops - 10)*blen*1000) / (double)tregex : -1;
- System.out.println(String.format(" : %8.3f mb/s", mbps));
+ System.out.println(String.format("%8.3f mb/s", mbps));
} else {
int count = 0;
final byte[] delimiters = new byte[] {'|','\n'};
diff --git a/src/com/characterforming/jrte/test/TestRunner.java b/src/com/characterforming/jrte/test/TestRunner.java
index d4257c4..252e211 100644
--- a/src/com/characterforming/jrte/test/TestRunner.java
+++ b/src/com/characterforming/jrte/test/TestRunner.java
@@ -82,7 +82,6 @@ public static void main(final String[] args) {
}
t1 = System.nanoTime() - t0;
if (i >= 10) {
- System.out.print(String.format("%4d", t1/1000000));
t2 += t1;
}
assert !trex.status().isRunnable();
@@ -91,7 +90,7 @@ public static void main(final String[] args) {
}
}
double mbps = (t2 > 0) ? (double)(100000000l*1000000000l) / (double)(t2*1024*1024) : -1;
- System.out.println(String.format(" : %8.3f mb/s (bytes)", mbps));
+ System.out.println(String.format("%8.3f mb/s (bytes)", mbps));
}
exitCode = 0;
} catch (Exception e) {
diff --git a/src/com/characterforming/ribose/IParameterizedEffector.java b/src/com/characterforming/ribose/IParameterizedEffector.java
index 57a9189..edf06d1 100644
--- a/src/com/characterforming/ribose/IParameterizedEffector.java
+++ b/src/com/characterforming/ribose/IParameterizedEffector.java
@@ -52,13 +52,13 @@
* {@link BaseParameterizedEffector}, which all {@code IParameterizedEffector}
* implementations must extend:
*
- * - model calls subclass.allocateParameters(int n)
+ *
- model calls subclassEffector.allocateParameters(int n)
*
- *
- model calls superclass.compileParameters(IToken[][], List)
- *
- sets P[i] = subclassEffector.compileParameter(IToken[]) for i<n
- * - model calls subclassEffector.passivate()
- *
- if overridden, subclass must call superclass.passivate()
- *
- BaseEffector.target = null
- BaseEffector.output = null
+ * - model calls superclassEffector.compileParameters(IToken[][], List)
+ *
- sets P[i] = subclassEffector.compileParameter(IToken[]) for i<n
+ * - model calls subclassEffector.passivate()
+ *
- if overridden, subclassEffector must call superclassEffector.passivate()
+ *
- superclassEffector.target = null
- superclassEffector.output = null
*
*
* Proxy effectors may receive calls to {@link #showParameterType()} and {@link
diff --git a/src/com/characterforming/ribose/ITransductor.java b/src/com/characterforming/ribose/ITransductor.java
index 293f9a8..b8ef1be 100644
--- a/src/com/characterforming/ribose/ITransductor.java
+++ b/src/com/characterforming/ribose/ITransductor.java
@@ -191,7 +191,7 @@ public boolean isStopped() {
/**
* Status == PROXY
* @return true if Status == PROXY
- */
+ */
public boolean isProxy() {
return this.equals(PROXY);
}
@@ -205,20 +205,15 @@ public class Metrics {
/** Number of {@code nul} signals injected */
public long errors;
- /** Number of bytes consumed in mproduct traps */
- public long product;
-
- /** Number of bytes consumed in msum traps */
- public long sum;
-
- /** Number of bytes consumed in mscan traps */
- public long scan;
-
/** Number of bytes allocated while marking */
public long allocated;
- /** Constructor */
+ /** Trap count and number of bytes consumed in traps */
+ public long[][] traps;
+
+ /** Constructor */
public Metrics() {
+ this.traps = new long[4][2];
this.reset();
}
@@ -228,7 +223,10 @@ public Metrics() {
* @return a reference to the reset metrics
*/
public Metrics reset() {
- bytes = errors = product = sum = scan = 0;
+ for (int mode = 0; mode < this.traps.length; mode++) {
+ this.traps[mode][0] = this.traps[mode][1] = 0;
+ }
+ bytes = errors = allocated = 0;
return this;
}
@@ -238,12 +236,16 @@ public Metrics reset() {
* @param accumulator the metrics to be updated
*/
public void update(Metrics accumulator) {
- accumulator.bytes += this.bytes;
- accumulator.errors += this.errors;
- accumulator.product += this.product;
- accumulator.sum += this.sum;
- accumulator.scan += this.scan;
- accumulator.allocated += this.allocated;
+ if (accumulator != null) {
+ accumulator.bytes += this.bytes;
+ accumulator.errors += this.errors;
+ accumulator.allocated += this.allocated;
+ for (int mode = 0; mode < this.traps.length; mode++) {
+ accumulator.traps[mode][0] += this.traps[mode][0];
+ accumulator.traps[mode][1] += this.traps[mode][1];
+ }
+ }
+ this.reset();
}
}
@@ -365,10 +367,10 @@ ITransductor run()
/**
* Update metrics from the most recent {@link #run()} call. Metrics are
* preserved until the {@code run()} method is called again. This method
- * sums the metrics from the most recent {@link #run()} call into an
- * accumulating Metrics instance.
+ * sums the transductor metrics from the most recent {@link #run()} call
+ * into an accumulating Metrics instance and resets the transductor metrics.
*
- * @param metrics the metrics to be updated
+ * @param metrics the metrics to be updated (or null to reset transductor metrics)
*/
void metrics(Metrics metrics);