From 9a1a112089e0480204f7ef843c2e439f1db1e4a8 Mon Sep 17 00:00:00 2001 From: jrte Date: Fri, 3 Nov 2023 20:02:14 -0300 Subject: [PATCH] Fixes #40, fixes #41 - iToken (interface changes) - removed getType(), getReference() - added isLiteral(), isField(), isSignal(), isTransducer() - issue fixes and dependent changes in other files - added .bin (VScode build output) to .gitignore Signed-off-by: jrte --- .gitignore | 1 + build.xml | 7 +- patterns/test/ValuesTest.inr | 2 +- .../jrte/engine/Assembler.java | 91 ++--- .../jrte/engine/BaseFieldEffector.java | 18 +- .../jrte/engine/BaseInputOutputEffector.java | 10 +- .../jrte/engine/ModelCompiler.java | 28 +- .../characterforming/jrte/engine/Token.java | 322 +++++++++++++----- .../jrte/engine/Transductor.java | 52 +-- .../jrte/test/FileRunner.java | 7 +- src/com/characterforming/ribose/IToken.java | 145 ++++---- .../base/BaseParameterizedEffector.java | 24 +- 12 files changed, 410 insertions(+), 297 deletions(-) diff --git a/.gitignore b/.gitignore index ddc55b7..31b39f7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ regression.* **/.fuse_hidden* **/.~* **/*.bak +.bin .data/ .doc/ .tmp/ diff --git a/build.xml b/build.xml index 1989345..abd0c9d 100755 --- a/build.xml +++ b/build.xml @@ -48,9 +48,10 @@ - - - + + + + diff --git a/patterns/test/ValuesTest.inr b/patterns/test/ValuesTest.inr index 856f472..124ef87 100644 --- a/patterns/test/ValuesTest.inr +++ b/patterns/test/ValuesTest.inr @@ -9,7 +9,7 @@ ValuesTest = ( | (dot, paste) (digit, paste)* (space|nl, real paste out clear) | ((black - {digit,dash,dot}), paste) (black, paste) (space|nl, string paste out clear) ) - ((pass, out[`!!pass; `]) | (fail, out[`!!fail; `])) + ((pass, out[`\xF8!pass; `]) | (fail, out[`\xF8!fail; `])) )* ):dfamin; diff --git a/src/com/characterforming/jrte/engine/Assembler.java b/src/com/characterforming/jrte/engine/Assembler.java index 153063a..449744a 100644 --- a/src/com/characterforming/jrte/engine/Assembler.java +++ b/src/com/characterforming/jrte/engine/Assembler.java @@ -119,15 +119,18 @@ Assembly assemble(final int[][][] transitionMatrix, HashMap effec } else if (nextState.isSumState()) { transition[1] = this.injectSumEffector(nextState.idempotentBytes, fst.matrix(), state, eq); - } else if (nextState.isProductState() - && this.walk(nextState, walkedStates, fst, walkResult).get(0) >= ModelCompiler.MIN_PRODUCT_LENGTH) { - assert nextState.idempotentCount >= 255; - transition[1] = this.injectProductEffector(this.product(walkResult), walkResult.get(1), - fst.matrix(), state, eq); + } else if (nextState.isProductState()) { + this.walk(nextState, walkedStates, fst, walkResult); + if (walkResult.get(0) >= ModelCompiler.MIN_PRODUCT_LENGTH) { + assert nextState.idempotentCount >= 255; + transition[0] = walkResult.get(1); + transition[1] = this.injectProductEffector(walkResult, + fst.matrix(),state, eq); + } } + if (transition[1] < 0) + markedEffects[-1 * transition[1]] = true; } - if (transition[1] < 0) - markedEffects[-1 * transition[1]] = true; } } @@ -221,10 +224,9 @@ private Fst reduceEquivalentInputs(int[][][] transitionMatrix) { assert transitionMatrix[token].length == transitionMatrix[0].length; final IntsArray transitions = new IntsArray(transitionMatrix[token]); HashSet equivalentInputOrdinals = equivalenceSets.computeIfAbsent( - transitions, absent -> new HashSet<>(10)); - if (equivalentInputOrdinals.isEmpty()) { + transitions, absent -> new HashSet<>(10)); + if (equivalentInputOrdinals.isEmpty()) equivalenceSets.put(transitions, equivalentInputOrdinals); - } equivalentInputOrdinals.add(token); } @@ -245,7 +247,7 @@ private Fst reduceEquivalentInputs(int[][][] transitionMatrix) { for (int state = 0; state < nStates; state++) { for (int eq = 0; eq < nInputs; eq++) matrix[state][eq] = transitionMatrix[equiv[eq].iterator().next().intValue()][state]; - states[state] = new State(state, matrix[state], + states[state] = new State(state, matrix[state], equiv, this.compiler.getSignalLimit()); } @@ -259,11 +261,12 @@ private int mark(int[][][] matrix, boolean[] markedStates) { int marked = 0; while (!stack.isEmpty()) { int state = stack.pop(); - markedStates[state] = true; - for (int[] transition : matrix[state]) - if (!markedStates[transition[0]]) + if (!markedStates[state]) { + markedStates[state] = true; + for (int[] transition : matrix[state]) stack.push(transition[0]); - ++marked; + ++marked; + } } return marked; } @@ -278,15 +281,14 @@ private int injectEffector(int action, int effector, int parameter) { Transducer.parameter(action), -1 * effector, parameter, 0 }; } else if (action > NUL) { - key = new int[] { - action, -1 * effector, parameter, 0 }; + key = new int[] { action, -1 * effector, parameter, 0 }; } else if (action < NUL) { key = this.effectVectors.get(-1 * action); key = Arrays.copyOf(key, key.length + 2); key[key.length - 3] = -1 * effector; key[key.length - 2] = parameter; key[key.length - 1] = 0; - } else key = null; + } if (key != null) { action = -1 * this.effectVectorMap.computeIfAbsent( new Ints(key), absent -> this.effectVectorMap.size()); @@ -296,32 +298,33 @@ private int injectEffector(int action, int effector, int parameter) { return action; } - private int injectScanEffector(int idempotentByte, int[][][] matrix, State state, int eq) { - assert idempotentByte >= 0 && idempotentByte < 256; - byte scanByte = (byte)(idempotentByte & 0xff); - Argument argument = new Argument(-1, - new BytesArray(new byte[][] { { scanByte } })); + private int injectScanEffector(int token, int[][][] matrix, State state, int eq) { + byte[] scan = new byte[] { Token.escape(), (byte) (token & 0xff) }; + Argument argument = new Argument(-1, new BytesArray(new byte[][] { scan })); return this.injectEffector(matrix[state.ordinal][eq][1], mscanOrdinal, this.compiler.compileParameters(mscanOrdinal, argument)); } - private int injectSumEffector(long[] idempotentBitmap, int[][][] matrix, State state, int eq) { - int selfCount = 0; - byte[] selfBytes = new byte[256]; - for (int word = 0; word < idempotentBitmap.length; word++) + private int injectSumEffector(long[] bitmap, int[][][] matrix, State state, int eq) { + int n = 0; + byte[] sum = new byte[256]; + sum[n++] = Token.escape(); + for (int word = 0; word < bitmap.length; word++) for (int bit = 0; bit < 64; bit++) - if (0 != ((1L << bit) & idempotentBitmap[word])) - selfBytes[selfCount++] = (byte)(64 * word + bit); - Argument argument = new Argument(-1, - new BytesArray(new byte[][] { Arrays.copyOf(selfBytes, selfCount) })); + if (0 != ((1L << bit) & bitmap[word])) + sum[n++] = (byte) (64 * word + bit); + sum = Arrays.copyOf(sum, n); + Argument argument = new Argument(-1, new BytesArray(new byte[][] { sum })); return this.injectEffector(matrix[state.ordinal][eq][1], msumOrdinal, this.compiler.compileParameters(msumOrdinal, argument)); } - private int injectProductEffector(byte[] product, int endpoint, int[][][] matrix, State state, int eq) { - Argument argument = new Argument(-1, - new BytesArray(new byte[][] { Arrays.copyOf(product, product.length) })); - matrix[state.ordinal][eq][0] = endpoint; + private int injectProductEffector(ArrayList walkResult, int[][][] matrix, State state, int eq) { + byte[] product = new byte[walkResult.size() - 2]; + product[0] = Token.escape(); + for (int i = 1; i < product.length; i++) + product[i] = (byte) (walkResult.get(i + 1).intValue() & 0xff); + Argument argument = new Argument(-1, new BytesArray(new byte[][] { product })); return this.injectEffector(matrix[state.ordinal][eq][1], mproductOrdinal, this.compiler.compileParameters(mproductOrdinal, argument)); } @@ -331,8 +334,19 @@ private ArrayList walk(State nextState, boolean[] walkedStates, Fst fst walkResult.add(0); walkResult.add(-1); ArrayList walkStates = new ArrayList<>(16); Arrays.fill(walkedStates, false); + int[] transition = nextState.transitions[fst.inputEquivalenceIndex[Signal.NUL.signal()]]; + int[] tx = new int[] { transition[0] != nextState.ordinal ? transition[0] : -1, transition[1] }; + int[] ty = new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE }; while (nextState.isProductState() && !walkedStates[nextState.ordinal]) { assert nextState.outboundByte == (nextState.outboundByte & 0xff); + transition = nextState.transitions[fst.inputEquivalenceIndex[Signal.NUL.signal()]]; + ty[0] = transition[0] != nextState.ordinal ? transition[0] : -1; + ty[1] = transition[1]; + if ((tx[0] != ty[0]) || (tx[1] != ty[1])) { + while (walkResult.size() > 2) + walkResult.remove(2); + return walkResult; + } walkResult.add(nextState.outboundByte); walkedStates[nextState.ordinal] = true; walkStates.add(nextState.ordinal); @@ -346,13 +360,6 @@ private ArrayList walk(State nextState, boolean[] walkedStates, Fst fst return walkResult; } - private byte[] product(ArrayList walkResult) { - byte[] p = new byte[walkResult.size() - 3]; - for (int i = 0; i < p.length; i++) - p[i] = (byte)(walkResult.get(i + 2).intValue() & 0xff); - return p; - } - @SuppressWarnings("unchecked") private HashSet[] allocateHashSetArray(int size) { return (HashSet[])new HashSet[size]; diff --git a/src/com/characterforming/jrte/engine/BaseFieldEffector.java b/src/com/characterforming/jrte/engine/BaseFieldEffector.java index 19aa4c2..02d97e5 100644 --- a/src/com/characterforming/jrte/engine/BaseFieldEffector.java +++ b/src/com/characterforming/jrte/engine/BaseFieldEffector.java @@ -1,18 +1,18 @@ /*** * Ribose is a recursive transduction engine for Java - * + * * Copyright (C) 2011,2022 Kim Briggs - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program (LICENSE-gpl-3.0). If not, see * . @@ -30,14 +30,14 @@ * Base class for parameterized field effectors, which are invoked with * field name parameters. The setParamater(int, charset, byte[][]), invoke(), and * invoke(int) methods must be implemented by subclasses. - * + * * @author Kim Briggs */ abstract class BaseFieldEffector extends BaseParameterizedEffector { /** * Constructor - * - * @param transductor The transductor target that binds the effector + * + * @param transductor The transductor target that binds the effector * @param name the field name * @throws CharacterCodingException */ @@ -53,10 +53,10 @@ public Integer[] allocateParameters(int parameterCount) { @Override // IParameterizedEffector#compileParameter(IToken[]) public Integer compileParameter(final IToken[] parameterList) throws TargetBindingException { if (parameterList.length != 1) { - throw new TargetBindingException(String.format("%1$s.%2$s[]: effector accepts exactly one parameter", + throw new TargetBindingException(String.format("%1$s.%2$s[]: effector accepts exactly one parameter", super.target.getName(), super.getName())); } - if (parameterList[0].getType() != IToken.Type.FIELD) { + if (!parameterList[0].isField()) { throw new TargetBindingException(String.format("%1$s.%2$s[]: effector accepts only a FIELD parameter", super.target.getName(), super.getName())); } diff --git a/src/com/characterforming/jrte/engine/BaseInputOutputEffector.java b/src/com/characterforming/jrte/engine/BaseInputOutputEffector.java index 7093504..06588e8 100644 --- a/src/com/characterforming/jrte/engine/BaseInputOutputEffector.java +++ b/src/com/characterforming/jrte/engine/BaseInputOutputEffector.java @@ -60,11 +60,11 @@ public IToken[] compileParameter(final IToken[] parameterList) throws TargetBind super.target.getName(), super.getName())); } for (IToken token : parameterList) { - if (token.getType() != IToken.Type.LITERAL && token.getType() != IToken.Type.FIELD) { - throw new TargetBindingException(String.format( - "%1$s.%2$s[]: literal or field name expected, found '%3$s'", - super.target.getName(), super.getName().asString(), - token.asString())); + if (!token.isLiteral() && !token.isField()) { + throw new TargetBindingException(String.format( + "%1$s.%2$s[]: literal or field name expected, found '%3$s'", + super.target.getName(), super.getName().asString(), + token.asString())); } } return parameterList; diff --git a/src/com/characterforming/jrte/engine/ModelCompiler.java b/src/com/characterforming/jrte/engine/ModelCompiler.java index d299126..485d9ed 100755 --- a/src/com/characterforming/jrte/engine/ModelCompiler.java +++ b/src/com/characterforming/jrte/engine/ModelCompiler.java @@ -45,7 +45,6 @@ import com.characterforming.ribose.ITarget; import com.characterforming.ribose.ITransduction; import com.characterforming.ribose.ITransductor; -import com.characterforming.ribose.IToken.Type; import com.characterforming.ribose.base.BaseEffector; import com.characterforming.ribose.base.Bytes; import com.characterforming.ribose.base.Codec; @@ -62,8 +61,8 @@ public final class ModelCompiler extends Model implements ITarget, AutoCloseable private static final String AUTOMATON = "Automaton"; private static final String AMBIGUOUS_STATE_MESSAGE = "%1$s: Ambiguous state %2$d"; - static final int MIN_PRODUCT_LENGTH = Integer.parseInt(System.getProperty("ribose.product.threshold", "10")); - static final int MIN_SUM_SIZE = Integer.parseInt(System.getProperty("ribose.sum.threshold", "128")); + static final int MIN_PRODUCT_LENGTH = Integer.parseInt(System.getProperty("ribose.product.threshold", "-1")); + static final int MIN_SUM_SIZE = Integer.parseInt(System.getProperty("ribose.sum.threshold", "-1")); static final int MIN_SCAN_SIZE = 255; private Bytes transducerName; @@ -405,7 +404,7 @@ private boolean compileTransducer(File inrFile) { private boolean validate() { for (Token token : this.tapeTokens.get(0)) { - if (token.getType() != Type.LITERAL && token.getType() != Type.SIGNAL) { + if (!token.isLiteral() && !token.isSignal()) { this.addError(String.format("Error: Invalid %2$s token '%1$s' on tape 0", token.asString(), token.getTypeName())); } else if (token.getSymbol().bytes().length > 1 @@ -415,7 +414,7 @@ private boolean validate() { } } for (Token token : this.tapeTokens.get(1)) { - if (token.getType() != Type.LITERAL) { + if (!token.isLiteral()) { this.addError(String.format("Error: Invalid %2$s token '%1$s' on tape 1", token.asString(), token.getTypeName())); } else if (this.getEffectorOrdinal(token.getSymbol()) < 0) { @@ -424,11 +423,11 @@ private boolean validate() { } } for (Token token : this.tapeTokens.get(2)) { - if (token.getType() == Type.TRANSDUCER + if (token.isTransducer() && super.getTransducerOrdinal(token.getSymbol()) < 0) { this.addError(String.format("Error: Unrecognized transducer token '%1$s' on tape 1", token.asString())); - } else if (token.getType() == Type.SIGNAL + } else if (token.isSignal() && super.getSignalOrdinal(token.getSymbol()) > Signal.EOS.signal() && !this.tapeTokens.get(0).contains(token)) { this.addError(String.format("Error: Signal token '%1$s' on tape 2 is never referenced on tape 0", @@ -510,18 +509,17 @@ void putTransition(Transition transition) { if (transition.tape == 1 || transition.symbol.getLength() > 1) { Token token = new Token(transition.symbol.bytes(), -1, transducerOrdinal); Bytes symbol = token.getSymbol(); - Type type = token.getType(); - if (transition.tape == 0 && type == Type.LITERAL && transition.symbol.getLength() > 1) { - super.addSignal(symbol); - this.tapeTokens.get(0).add(new Token(token.getReference(Type.SIGNAL, symbol.bytes()))); + if (transition.tape == 0 && token.isLiteral() && transition.symbol.getLength() > 1) { + this.tapeTokens.get(0).add(new Token(Token.reference(Token.Type.SIGNAL, symbol.bytes()), + super.addSignal(symbol), transducerOrdinal)); } else if (transition.tape == 1) { this.tapeTokens.get(1).add(token); - } else if (transition.tape == 2 && type != Type.LITERAL) { - if (type == Type.FIELD) { + } else if (transition.tape == 2 && !token.isLiteral()) { + if (token.isField()) { token.setOrdinal(super.addLocalField(this.transducerOrdinal, super.addField(symbol))); - } else if (type == Type.TRANSDUCER) { + } else if (token.isTransducer()) { token.setOrdinal(super.addTransducer(symbol)); - } else if (type == Type.SIGNAL) { + } else if (token.isSignal()) { token.setOrdinal(super.addSignal(symbol)); } this.tapeTokens.get(2).add(token); diff --git a/src/com/characterforming/jrte/engine/Token.java b/src/com/characterforming/jrte/engine/Token.java index fe1050d..eea787f 100644 --- a/src/com/characterforming/jrte/engine/Token.java +++ b/src/com/characterforming/jrte/engine/Token.java @@ -1,87 +1,116 @@ /*** * Ribose is a recursive transduction engine for Java - * + * * Copyright (C) 2011,2022 Kim Briggs - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program (LICENSE-gpl-3.0). If not, see * . */ package com.characterforming.jrte.engine; +import java.nio.charset.CharacterCodingException; import java.util.Arrays; import java.util.HashMap; - import com.characterforming.jrte.engine.Model.Argument; import com.characterforming.ribose.IToken; import com.characterforming.ribose.base.Bytes; +import com.characterforming.ribose.base.Codec; /** - * Wrapper for raw effector parameter tokens referenced in model effector parameters. - * + * Wrapper for raw effector parameter tokens referenced in model effector parameters. + * * @author Kim Briggs */ -final class Token implements IToken { +public final class Token implements IToken { + /** type decoration ('@') for ginr tokens representing transducers in ribose patterns */ + private static byte transducerType = '@'; + /** type decoration ('!') for ginr tokens representing signals in ribose patterns */ + private static byte signalType = '!'; + /** type decoration ('~') for ginr tokens representing fields in ribose patterns */ + private static byte fieldType = '~'; + /** type decoration (0xf8) for ginr escaped literal tokens */ + private static byte literalType = Token.escape(); + /** type decoration (0xf8) for ginr escaped literal tokens */ + private static byte nullType = (byte) 0x0; + + /** Enumeration of token types. */ + public enum Type { + /** A literal token, eg {@code `abc`} or {@code `0xf8!nil`} (escaped signal reference) */ + LITERAL(literalType, "literal"), + /** A transducer token, eg {@code `@abc`} */ + TRANSDUCER(transducerType, "transducer"), + /** A field token, eg {@code `~abc`} */ + FIELD(fieldType, "field"), + /** A signal token, eg {@code `!abc`} */ + SIGNAL(signalType, "signal"), + /** A null or empty token */ + NULL(nullType, "null"); + + + private final byte indicator; + private final String name; + + /** + * Constructor + * + * @param indicator the type indicator (reference prefix) + * @param name the display name for the Type + */ + Type(byte indicator, String name) { + assert Type.isIndicator(indicator); + this.indicator = indicator; + this.name = name; + } + + /** + * Get the type indicator (reference prefix) + * + * @return the type indicator + */ + public byte getIndicator() { + return this.indicator; + } + + /** + * Get the type name + * + * @return the type name + */ + public String getName() { + return this.name; + } + + public static boolean isIndicator(byte b) { + return (b == Token.literalType) || (b == Token.transducerType) + || (b == Token.fieldType) || (b == Token.signalType) + || (b == Token.nullType); + } + } + private static final String[] types = { "literal", "transducer", "field", "signal" }; - private final IToken.Type type; + private final Token.Type type; private final Bytes literal; private final Bytes symbol; private int transducerOrdinal; private int tokenOrdinal; - /** - * Assemble tokens from effector parameter bytes. For internal use - * by Model to support effector parameter precompilation during - * model assembly and runtime use. Here a parameter a series of - * raw byte arrays scraped from a paramterized effector reference - * in a ribose pattern, eg for {@code count[`99` `!nil`])} the - * raw tokens {@code `99`} (literal) and {@code `!nil`} (signal) - * are compiled to an instance of the {@code count} effector's - * effector parameter type (int[2]) as {@code new int[] {99, 257}}. - * - * @param model the containing ribose model - * @param argument a series of raw effector parameter tokens - */ - public static IToken[] getParameterTokens(Model model, Argument argument) { - byte[][] bytes = argument.tokens().getBytes(); - IToken[] tokens = new Token[bytes.length]; - for (int i = 0; i < tokens.length; i++) { - Token token = new Token(argument.tokens().getBytes(i)); - if (token.type == Type.TRANSDUCER) { - tokens[i] = new Token(bytes[i], model.getTransducerOrdinal(token.symbol)); - } else if (token.type == Type.FIELD) { - int transducerOrdinal = argument.transducerOrdinal(); - int fieldOrdinal = model.getFieldOrdinal(token.symbol); - HashMap localFieldMap = model.transducerFieldMaps.get(transducerOrdinal); - int localFieldIndex = localFieldMap.computeIfAbsent(fieldOrdinal, absent -> localFieldMap.size()); - tokens[i] = new Token(bytes[i], localFieldIndex); - assert transducerOrdinal >= 0 && fieldOrdinal >= 0 - && (localFieldMap.isEmpty() || localFieldIndex >= 0); - } else if (token.type == Type.SIGNAL) { - tokens[i] = new Token(bytes[i], model.getSignalOrdinal(token.symbol)); - } else { - tokens[i] = token; - } - } - return tokens; - } - /** * Constructor for literal tokens - * + * * @param token a literal token */ public Token(byte[] token) { @@ -91,7 +120,7 @@ public Token(byte[] token) { /** * Constructor for literal or symbolic tokens. The ordinal value is * ignored if the token does not represent a symbol. - * + * * @param token a literal or symbolic token * @param ordinal the symbolic token ordinal */ @@ -102,52 +131,53 @@ public Token(byte[] token, int ordinal) { /** * Constructor for literal or symbolic tokens. The ordinal value is * ignored if the token does not represent a symbol. - * + * * @param token a literal or symbolic token * @param ordinal the symbolic token ordinal * @param transducer the ordinal of the transducer the token is bound to */ public Token(byte[] token, int ordinal, int transducer) { this.type = Token.type(token); - if (this.type != IToken.Type.LITERAL) { - this.literal = new Bytes(token); - this.symbol = new Bytes(Arrays.copyOfRange(token, 1, token.length)); - this.transducerOrdinal = transducer; - this.tokenOrdinal = ordinal; + if (this.type != Token.Type.LITERAL) { + this.literal = new Bytes(Token.descape(token)); + this.symbol = new Bytes(Arrays.copyOfRange(this.literal.bytes(), 1, this.literal.getLength())); } else { - this.literal = new Bytes(this.literal(token)); + this.literal = new Bytes(Token.descape(token)); this.symbol = this.literal; - this.tokenOrdinal = -1; - this.transducerOrdinal = -1; } + this.transducerOrdinal = transducer; + this.tokenOrdinal = ordinal; } - private static Type type(byte[] token) { - Type t = IToken.Type.LITERAL; - if (token.length > 1 && token[0] != token[1]) { - if (token[0] == IToken.TRANSDUCER_TYPE) { - t = IToken.Type.TRANSDUCER; - } else if (token[0] == IToken.FIELD_TYPE) { - t = IToken.Type.FIELD; - } else if (token[0] == IToken.SIGNAL_TYPE) { - t = IToken.Type.SIGNAL; - } - } else if (token.length == 1 && token[0] == IToken.FIELD_TYPE) { - t = IToken.Type.FIELD; - } - return t; + @Override + public boolean isLiteral() { + return this.type == Type.LITERAL; } - @Override // @see com.characterforming.ribose.IToken#getType() - public Type getType() { - return this.type; + @Override + public boolean isField() { + return this.type == Type.FIELD; + } + + @Override + public boolean isSignal() { + return this.type == Type.SIGNAL; + } + + @Override + public boolean isTransducer() { + return this.type == Type.TRANSDUCER; } - + @Override // @see com.characterforming.ribose.IToken#asString() public String asString() { - return this.literal.asString(); + try { + return Codec.decode(this.symbol.bytes()); + } catch (CharacterCodingException e) { + return this.symbol.toHexString(); + } } - + @Override // @see com.characterforming.ribose.IToken#getLiteralValue() public Bytes getLiteral() { return this.literal; @@ -175,8 +205,7 @@ public int getTransducerOrdinal() { @Override public boolean equals(Object other) { - return other instanceof Token o - && this.type == o.type && this.symbol.equals(o.symbol); + return other instanceof Token o && this.type == o.type && this.symbol.equals(o.symbol); } @Override @@ -184,24 +213,133 @@ public int hashCode() { return this.symbol.hashCode() * (this.type.ordinal() + 1); } - void setOrdinal(int ordinal) { - this.tokenOrdinal = ordinal; + /** + * Make a reference (eg {@code `!name`}) for a symbol (eg {@code `name`}) + * + * @param type the type of reference to make + * @param symbol the symbal to reference + * @return the symbol reference + */ + static byte[] reference(Token.Type type, byte[] symbol) { + byte[] reference = new byte[symbol.length + 1]; + System.arraycopy(symbol, 0, reference, 1, symbol.length); + reference[0] = type.getIndicator(); + return reference; } - void setTransducerOrdinal(int transducerOrdinal) { - this.transducerOrdinal = transducerOrdinal; + /** + * The disinquished byte used to escape symbolic references in backquoted effector + * parameter tokens. For example, using {@code `\xf8@whatever`} to interpret token + * literally, not as a transducer reference, or prepending {@code 0xf8} to a series + * of binary bytes that might lead with a reference indicator byte. + * + * @return the escape byte (0xf8) + */ + static byte escape() { + return (byte) 0xf8; } - private byte[] literal(byte[] token) { - if ((token.length > 1) && (token[0] == token[1]) - && (token[0] == '!' || token[0] == '~' || token[0] == '@')) { - int trim = 2; - while (token[0] == token[trim]) { - trim++; - } - trim -= 1; - return Arrays.copyOfRange(token, trim, token.length); + /** + * A literal token must be escaped if its length is >1 and lead byte is a reference byte + * (!, @, ~ or 0xf8). + * + * @param token the token to test + * @return true if the token must be escaped + */ + static boolean mustEscape(byte[] token) { + return token.length > 1 + && (token[0] == Token.escape() || token[0] == '!' || token[0] == '@' || token[0] == '~'); + } + + /** + * A literal token is escaped if its length is >1 and lead byte the escape byte 0xf8 + * + * @param token the token to test + * @return true if the token escaped + */ + static boolean isEscaped(byte[] token) { + return token.length > 1 && token[0] == Token.escape(); + } + + /** + * A literal token is escaped by inserting, if necessary, the escape byte 0xf8 as + * lead byte to distinguish it unambiguously from symbolic reference tokens + * (fields, transducers, signals). The input token is returned unchanged if + * not ambiguous. + * + * @param token the token to escape + * @return a token that can be safely distinguished from symbolic refernce tokens + */ + static byte[] escape(byte[] token, int length) { + if (mustEscape(token)) { + byte[] t = new byte[length + 1]; + System.arraycopy(token, 0, t, 1, length); + t[0] = Token.escape(); + token = t; } return token; } + + /** + * A literal token is descaped by removing the lead escape byte 0xf8, or returned + * unchanged if not escaped. + * + * @param token the token to remove from escapement + * @return the literal token with lescape byte removed + */ + static byte[] descape(byte[] token) { + if (isEscaped(token)) { + byte[] t = new byte[token.length - 1]; + System.arraycopy(token, 1, t, 0, token.length - 1); + token = t; + } + return token; + } + + static IToken[] getParameterTokens(Model model, Argument argument) { + byte[][] bytes = argument.tokens().getBytes(); + IToken[] tokens = new Token[bytes.length]; + for (int i = 0; i < tokens.length; i++) { + Token token = new Token(argument.tokens().getBytes(i)); + if (token.type == Token.Type.FIELD) { + int transducerOrdinal = argument.transducerOrdinal(); + int fieldOrdinal = model.getFieldOrdinal(token.symbol); + HashMap localFieldMap = model.transducerFieldMaps.get(transducerOrdinal); + int localFieldIndex = localFieldMap.computeIfAbsent(fieldOrdinal, absent -> localFieldMap.size()); + tokens[i] = new Token(bytes[i], localFieldIndex); + assert transducerOrdinal >= 0 && fieldOrdinal >= 0 + && (localFieldMap.isEmpty() || localFieldIndex >= 0); + } else if (token.type == Token.Type.TRANSDUCER) + tokens[i] = new Token(bytes[i], model.getTransducerOrdinal(token.symbol)); + else if (token.type == Token.Type.SIGNAL) + tokens[i] = new Token(bytes[i], model.getSignalOrdinal(token.symbol)); + else + tokens[i] = token; + } + return tokens; + } + + private static Token.Type type(byte[] token) { + Token.Type type = Token.Type.LITERAL; + if (!Token.isEscaped(token)) { + if (token.length > 0) { + if (token[0] == Token.transducerType) + type = Token.Type.TRANSDUCER; + else if (token[0] == Token.fieldType) + type = Token.Type.FIELD; + else if (token[0] == Token.signalType) + type = Token.Type.SIGNAL; + } + } else if (token.length == 1 && token[0] == Token.fieldType) + type = Token.Type.FIELD; + return type; + } + + void setOrdinal(int ordinal) { + this.tokenOrdinal = ordinal; + } + + void setTransducerOrdinal(int transducerOrdinal) { + this.transducerOrdinal = transducerOrdinal; + } } diff --git a/src/com/characterforming/jrte/engine/Transductor.java b/src/com/characterforming/jrte/engine/Transductor.java index cd0fe41..d156bf3 100644 --- a/src/com/characterforming/jrte/engine/Transductor.java +++ b/src/com/characterforming/jrte/engine/Transductor.java @@ -785,13 +785,13 @@ public int invoke() throws EffectorException { public int invoke(final int parameterIndex) throws EffectorException { for (IToken t : super.parameters[parameterIndex]) { if (t instanceof Token token) { - if (token.getType() == IToken.Type.FIELD) { + if (token.isField()) { Value field = transducerStack.value(token.getOrdinal()); if (field != null) { value.paste(field); } - } else if (token.getType() == IToken.Type.LITERAL) { - byte[] bytes = token.getLiteral().bytes(); + } else if (token.isLiteral()) { + byte[] bytes = token.getSymbol().bytes(); value.paste(bytes, bytes.length); } else { throw new EffectorException(String.format("Invalid token `%1$s` for effector '%2$s'", @@ -916,23 +916,25 @@ public Integer compileParameter(final IToken[] parameterList) throws TargetBindi if (parameterList.length != 1) { throw new TargetBindingException("The signal effector accepts exactly one parameter"); } else if (parameterList[0] instanceof Token token) { - if (token.getType() == IToken.Type.SIGNAL) { + if (token.isSignal()) { int ordinal = token.getOrdinal(); if (ordinal < 0) { String signame; try { - byte[] sigbytes = token.getLiteral().bytes(); + byte[] sigbytes = token.getSymbol().bytes(); signame = Codec.decode(sigbytes); } catch (CharacterCodingException e) { signame = ""; } throw new TargetBindingException(String.format( - "Unkown signal reference for signal effector: %s", signame)); + "Unkown signal reference for signal effector: %s", + signame)); } return ordinal; } else { - throw new TargetBindingException(String.format("Invalid signal reference `%s` for signal effector, requires type indicator ('%c') before the transducer name", - token.asString(), IToken.SIGNAL_TYPE)); + throw new TargetBindingException(String.format( + "Invalid signal reference `%s` for signal effector, requires type indicator ('!') before the transducer name", + token.asString())); } } else { throw new TargetBindingException(String.format("Unknown IToken implementation class '%1$s'", @@ -957,12 +959,12 @@ public int invoke(final int parameterIndex) throws EffectorException { IToken[] parameters = super.parameters[parameterIndex]; byte[][] tokens = new byte[parameters.length][]; for (int i = 0; i < parameters.length; i++) { - if (parameters[i].getType() == IToken.Type.FIELD) { + if (parameters[i].isField()) { Value field = transducerStack.value(parameters[i].getOrdinal()); tokens[i] = (field != null) ? field.value() : Bytes.EMPTY_BYTES; } else { - assert parameters[i].getType() == IToken.Type.LITERAL; - tokens[i] = parameters[i].getLiteral().bytes(); + assert parameters[i].isLiteral(); + tokens[i] = parameters[i].getSymbol().bytes(); } } inputStack.put(tokens); @@ -985,14 +987,14 @@ public int invoke(final int parameterIndex) throws EffectorException { if (outputStream != null) { for (final IToken token : super.parameters[parameterIndex]) { try { - if (token.getType() == IToken.Type.FIELD) { + if (token.isField()) { Value field = transducerStack.value(token.getOrdinal()); if (field != null) { outputStream.write(field.value(), 0, field.length()); } } else { - assert token.getType() == IToken.Type.LITERAL; - byte[] data = token.getLiteral().bytes(); + assert token.isLiteral(); + byte[] data = token.getSymbol().bytes(); outputStream.write(data, 0, data.length); } } catch (IOException e) { @@ -1038,16 +1040,16 @@ public int[] compileParameter(final IToken[] parameterList) throws TargetBinding super.target.getName(), super.getName())); } int count = -1; - if (parameterList[0].getType() == IToken.Type.FIELD) { + if (parameterList[0].isField()) { count = -1 - parameterList[0].getOrdinal(); - } else if (parameterList[0].getType() == IToken.Type.LITERAL) { - byte[] v = parameterList[0].getLiteral().bytes(); + } else if (parameterList[0].isLiteral()) { + byte[] v = parameterList[0].getSymbol().bytes(); count = Base.decodeInt(v, v.length); } else { throw new TargetBindingException(String.format("%1$s.%2$s[]: invalid field|counter for count effector", super.target.getName(), super.getName())); } - if (parameterList[1].getType() == IToken.Type.SIGNAL) { + if (parameterList[1].isSignal()) { int signalOrdinal = parameterList[1].getOrdinal(); assert signalOrdinal >= SIGNUL; return new int[] { count, signalOrdinal }; @@ -1077,10 +1079,10 @@ public Integer[] allocateParameters(int parameterCount) { public Integer compileParameter(final IToken[] parameterTokens) throws TargetBindingException { if (parameterTokens.length != 1) { throw new TargetBindingException("The start effector accepts only one parameter"); - } else if (parameterTokens[0].getType() != IToken.Type.TRANSDUCER) { + } else if (!parameterTokens[0].isTransducer()) { throw new TargetBindingException(String.format( - "Invalid transducer reference `%s` for start effector, requires type indicator ('%c') before the transducer name", - parameterTokens[0].toString(), IToken.TRANSDUCER_TYPE)); + "Invalid transducer reference `%s` for start effector, requires type indicator ('@') before the transducer name", + parameterTokens[0].toString())); } return parameterTokens[0].getOrdinal(); } @@ -1145,7 +1147,7 @@ public long[] compileParameter(final IToken[] parameterList) throws TargetBindin throw new TargetBindingException("The msum effector accepts at most one parameter (a byte array of length >1)"); } long[] byteMap = new long[] {0, 0, 0, 0}; - for (byte b : parameterList[0].getLiteral().bytes()) { + for (byte b : parameterList[0].getSymbol().bytes()) { int i = b & 0xff; byteMap[i >> 6] |= 1L << (i & 0x3f); } @@ -1224,8 +1226,8 @@ public byte[] compileParameter(final IToken[] parameterList) throws TargetBindin if (parameterList.length != 1) { throw new TargetBindingException("The mproduct effector accepts at most one parameter (a byte array of length >1)"); } - byte[] tokens = parameterList[0].getLiteral().bytes(); - return Arrays.copyOf(tokens, tokens.length); + byte[] product = parameterList[0].getSymbol().bytes(); + return Arrays.copyOf(product, product.length); } @Override @@ -1273,7 +1275,7 @@ public Integer compileParameter(final IToken[] parameterList) throws TargetBindi if (parameterList.length != 1) { throw new TargetBindingException("The mscan effector accepts at most one parameter (a byte array of length 1)"); } - return 0xff & parameterList[0].getLiteral().bytes()[0]; + return 0xff & parameterList[0].getSymbol().bytes()[0]; } @Override diff --git a/src/com/characterforming/jrte/test/FileRunner.java b/src/com/characterforming/jrte/test/FileRunner.java index 8fb3e5f..74fb3b0 100644 --- a/src/com/characterforming/jrte/test/FileRunner.java +++ b/src/com/characterforming/jrte/test/FileRunner.java @@ -133,21 +133,18 @@ public static void main(final String[] args) { } long bytes = 10 * byteLength; 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)); + System.out.println(String.format("%8.3f mb/s %7.3f nul/kb %16s %20s %17s", + mbps, ekb, ssum, sproduct, sscan)); assert bytes == 0 || bytes >= 10*byteLength; } else { try ( diff --git a/src/com/characterforming/ribose/IToken.java b/src/com/characterforming/ribose/IToken.java index a029152..e49bbc7 100644 --- a/src/com/characterforming/ribose/IToken.java +++ b/src/com/characterforming/ribose/IToken.java @@ -23,139 +23,108 @@ import com.characterforming.ribose.base.Bytes; /** - * Wrapper for raw effector parameter tokens referenced in model effector parameters. - * A token is backed by a byte array containing UTF-8 text and/or binary bytes - * corresponding to backquoted Unicode text in effector parameters in ribose patterns. - * To ribose a token may represent a literal or a symbolic reference to a transducer, - * field or signal prefixed with a special byte designating the type of the referent - * (@, ~ or !, respectively). Literal tokens that require a type prefix, (eg {@code - * out[`!Aliteral`]}) can escape the prefix by doubling it ({@code out[`!!Aliteral`]}). + * Wrapper for raw effector parameter tokens referenced in model effector parameters. In + * ribose patterns effector parameters are presented as a series of 1 or more backquoted + * tokens (`token`), each of which is either a literal sequence of bytes or a symbolic + * reference to a field, signal or transducer. A single lead byte distinquishes between + * different token type: `~` for a field reference ({@code `~field`}), `!` for a signal + * ({@code `!signal`}) and `@` for a transducer ({@code `@transducer`}). If a literal + * token must begin with one of these bytes it can be escaped by prepending {@code `\xf8`} + * (eg, {@code `\xf8@...`}). Similarly, if a token may contain binary data that may lead + * with a symbolic reference type byte it must be prepended with {@code `\xf8`} to prevent + * it from being misinterpreted. + *

+ * Briefly, any backquoted effefctor parameter token that has `~`, `!`, `@` or `\xf8` as + * lead byte must be escaped if it must be interpreted as a literal token. For example, + *
    + *
  • {@code out[`\xf8\xatext`]} -> {@code out[`\xf8\xf8\xatext`]} + *
  • {@code out[`@literal`]} -> {@code out[`\xf8@literal`]} + *
  • {@code out[`~literal`]} -> {@code out[`\xf8~literal`]} + *
  • {@code out[`!literal`]} -> {@code out[`\xf8!literal`]} + *
+ * The original literal bytes can be recovered by calling {@link #getSymbol()}, which + * can also be used to recover the undecorated field, signal or transducer name from + * non-literal tokens types. *

* Arrays of {@code IToken} objects, corresponding to effector parameters, are conveyed - * to proxy parameterized instances during effector parameter precompilation. See the - * {@link IParameterizedEffector} documentation for more information regarding parameter - * tokens. + * to proxy parameterized effectors for parameter precompilation. The proxy effector + * constructs from each {@code IToken[]} array an indexed instance of its generic parameter + * type P. See the {@link IParameterizedEffector} documentation for more information + * regarding effector parameter compilation. * * @author Kim Briggs * */ public interface IToken { - /** type decoration ('@') for ginr tokens representing transducers in ribose patterns */ - byte TRANSDUCER_TYPE = '@'; - /** type decoration ('!') for ginr tokens representing signals in ribose patterns */ - byte SIGNAL_TYPE = '!'; - /** type decoration ('~') for ginr tokens representing fields in ribose patterns */ - byte FIELD_TYPE = '~'; - /** type decoration (0) for ginr literal tokens */ - byte LITERAL_TYPE = 0; - - /** Enumeration of token types. */ - enum Type { - /** A literal token, eg `abc` */ - LITERAL(LITERAL_TYPE, "literal"), - /** A transducer token, eg `@abc` */ - TRANSDUCER(TRANSDUCER_TYPE, "transducer"), - /** A field token, eg `~abc` */ - FIELD(FIELD_TYPE, "field"), - /** A signal token, eg `!abc` */ - SIGNAL(SIGNAL_TYPE, "signal"); - - private final byte indicator; - private final String name; - - /** - * Constructor - * - * @param indicator the type indicator (reference prefix) - * @param name the display name for the Type - */ - Type(byte indicator, String name) { - this.indicator = indicator; - this.name = name; - } + /** + * Test for literal token + * + * @return true if this is a literal token + */ + boolean isLiteral(); - /** - * Get the type indicator (reference prefix) - * - * @return the type indicator - */ - byte getIndicator() { - return this.indicator; - } + /** + * Test for field token + * + * @return true if this is a field token + */ + boolean isField(); - /** - * Get the type indicator (reference prefix) - * - * @return the type indicator - */ - String getName() { - return this.name; - } - } + /** + * Test for signal token + * + * @return true if this is a signal token + */ + boolean isSignal(); /** - * Get the type of the token. - * - * @return the type of the token + * Test for transducer token + * + * @return true if this is a transducer token */ - Type getType(); + boolean isTransducer(); /** - * Get the a string representing the token. This will obtained by decoding the + * Get the a string representing the token. This will obtained by decoding the * token literal value as a UTF-8 byte sequence, or converting all bytes to * /xHH representation if decoding fails. - * + * * @return a string representation of this token */ String asString(); /** * Get the name of the type, eg "signal", "field" or "transducer". - * + * * @return the name of the type of this token */ String getTypeName(); /** - * Get the raw symbol bytes, including type prefix if not a literal symbol. + * Get the raw token bytes, including type prefix if not a literal symbol. * - * @return the raw symbol bytes, including type prefix if not a literal symbol + * @return the raw token bytes, including type prefix if not a literal symbol */ Bytes getLiteral(); /** - * Get the symbol bytes, excluding type prefix if not a literal symbol. For - * literal tokens this is identical to {@code getLiteral()}. + * Get the symbol bytes, excluding type prefix. * - * @return the symbol bytes, excluding type prefix if not a literal symbol + * @return the symbol bytes, excluding type prefix */ Bytes getSymbol(); /** * Get the symbol ordinal, or return -1 for literal tokens. - * + * * @return symbol ordinal, or -1 for literal tokens */ int getOrdinal(); /** * Get the ordinal of the transducer of this field token (-1 for other token types) - * + * * @return the transducer ordinal */ int getTransducerOrdinal(); - - /** - * Make a reference (eg `!name`) for a symbol (eg `name`) - * - * @param type the type of reference to make - * @param symbol the symbal to reference - * @return the symbol reference - */ - default byte[] getReference(Type type, byte[] symbol) { - byte[] reference = new byte[symbol.length + 1]; - System.arraycopy(symbol, 0, reference, 1, symbol.length); - reference[0] = type.getIndicator(); - return reference; - } } diff --git a/src/com/characterforming/ribose/base/BaseParameterizedEffector.java b/src/com/characterforming/ribose/base/BaseParameterizedEffector.java index 4018383..22330d1 100644 --- a/src/com/characterforming/ribose/base/BaseParameterizedEffector.java +++ b/src/com/characterforming/ribose/base/BaseParameterizedEffector.java @@ -1,18 +1,18 @@ /*** * Ribose is a recursive transduction engine for Java - * + * * Copyright (C) 2011,2022 Kim Briggs - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program (LICENSE-gpl-3.0). If not, see * . @@ -28,7 +28,7 @@ import com.characterforming.ribose.IToken; /** - * Base {@link IParameterizedEffector} implementation class extends {@link BaseEffector} + * Base {@link IParameterizedEffector} implementation class extends {@link BaseEffector} * to support specialized effectors. All {@link IParameterizedEffector} implementations * must extend {@link BaseParameterizedEffector} and must implement: *
    @@ -44,7 +44,7 @@ * {@link IParameterizedEffector} interface are for internal use only. These methods * implement the parameter compilation and binding protocols for all parameterized effector * implementations. - * + * * @param the effector target type * @param

    the effector parameter type, constructible from IToken[] (eg new P(IToken[])) * @author Kim Briggs @@ -55,10 +55,10 @@ public abstract class BaseParameterizedEffector extends Ba /** Raw and compiled effector parameters indexed and selected by parameter ordinal.*/ protected P[] parameters = null; private IToken[][] tokens = null; - + /** * Constructor - * + * * @param target the target for the effector * @param name the effector name as referenced from ginr transducers * @throws CharacterCodingException if encoder fails @@ -89,7 +89,7 @@ public String showParameterTokens(int parameterIndex) { StringBuilder sb = new StringBuilder(256); for (IToken token : this.tokens[parameterIndex]) { sb.append(sb.length() == 0 ? "`" : " `") - .append(token.getLiteral().asString()) + .append(token.getSymbol().asString()) .append("`"); } return sb.toString(); @@ -97,7 +97,7 @@ public String showParameterTokens(int parameterIndex) { /** * Set the parameters array from proxy effector - * + * * @param proxyEffector the proxy effector holding the compiled parameters */ @SuppressWarnings("unchecked") @@ -111,7 +111,7 @@ public final void setParameters(IParameterizedEffector proxyEffector) { /** * Get the number of parameters for this effector (after parameter compilation * is complete) - * + * * @return the parameter count * @see compileParameters(IToken[][], List) */ @@ -123,7 +123,7 @@ public final int getParameterCount() { * Allocate and populate the {@code parameters} array with precompiled * parameter ({@code P}) instances, compiled from a list of parameter * token arrays. This method is for internal use only. - * + * * @param parameterTokens an array of IToken[] (raw effector parameter tokens) * @param parameterErrors a list of error messages if one ore more parameters fail to compile * @return false if any errors were reported