diff --git a/src/com/characterforming/jrte/engine/ModelCompiler.java b/src/com/characterforming/jrte/engine/ModelCompiler.java index e3da77a..93f59b5 100755 --- a/src/com/characterforming/jrte/engine/ModelCompiler.java +++ b/src/com/characterforming/jrte/engine/ModelCompiler.java @@ -367,7 +367,6 @@ private boolean createModelFile(File riboseModelFile, Logger rtcLogger) { private boolean compileTransducer(File inrFile) throws RiboseException { int size = (int)inrFile.length(); byte[] bytes = null; - boolean fail = true; try (DataInputStream f = new DataInputStream(new FileInputStream(inrFile))) { this.reset(inrFile); int position = 0, length = size; @@ -381,11 +380,11 @@ private boolean compileTransducer(File inrFile) throws RiboseException { } catch (FileNotFoundException e) { this.addError(String.format("%1$s: File not found '%2$s'", this.transducerName, inrFile.getPath())); - return !fail; + return false; } catch (IOException e) { this.addError(String.format("%1$s: IOException compiling '%2$s'; %3$s", this.transducerName, inrFile.getPath(), e.getMessage())); - return !fail; + return false; } try { Bytes automaton = Codec.encode("Automaton"); @@ -397,15 +396,11 @@ private boolean compileTransducer(File inrFile) throws RiboseException { this.transductor.stop(); assert this.transductor.status().isStopped(); this.saveTransducer(); - fail = false; - } catch (ModelException | EffectorException | DomainErrorException e) { + } catch (ModelException | EffectorException | DomainErrorException | CharacterCodingException e) { this.addError(String.format("%1$s: Failed to compile '%2$s'; %3$s", this.transducerName, inrFile.getPath(), e.getMessage())); - } catch (CharacterCodingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); } - return !fail; + return true; } private boolean validate() { @@ -414,7 +409,7 @@ private boolean validate() { 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 - && !this.tapeTokens.get(2).contains(token)) { + && !this.tapeTokens.get(2).contains(token)) { this.addError(String.format("Error: Unrecognized signal reference '%1$s' on tape 0", token.asString())); } @@ -445,9 +440,8 @@ private boolean validate() { if (super.transducerNameIndex[ordinal] == null || !super.transducerNameIndex[ordinal].equals(e.getKey()) || super.transducerOffsetIndex[ordinal] <= 0) { - this.addError(String.format("'%1$s': referenced but not compiled in model", + this.addError(String.format("'%1$s': referenced but not compiled in model", e.getKey().asString())); - } } @@ -467,13 +461,10 @@ void saveTransducer() throws ModelException, CharacterCodingException { int nInputs = this.kernelMatrix.length; int nStates = this.kernelMatrix[0].length; int nTransitions = 0; - for (int input = 0; input < nInputs; input++) { - for (int state = 0; state < nStates; state++) { - if (this.kernelMatrix[input][state][1] != 0) { + for (int input = 0; input < nInputs; input++) + for (int state = 0; state < nStates; state++) + if (this.kernelMatrix[input][state][1] != 0) nTransitions++; - } - } - } final int transitionCount = nTransitions; final int fieldCount = super.getFieldCount(this.transducerOrdinal); double sparsity = 100 * (1 - ((double)nTransitions / (double)(nStates * nInputs))); @@ -569,12 +560,7 @@ void putAutomaton() throws CharacterCodingException { this.effectorVectorMap.put(new Ints(new int[] { 0 }), 0); for (final Integer inrInputState : inrInputStates) { for (final Transition t : this.getTransitions(inrInputState)) { - if (!t.isFinal) { - if (t.tape != 0) { - this.addError(String.format(ModelCompiler.AMBIGUOUS_STATE_MESSAGE, - this.getTransducerName(), t.from)); - continue; - } + if (t.tape == 0 && !t.isFinal) { try { final int rteState = this.inputStateMap.get(t.from); final int inputOrdinal = super.getInputOrdinal(t.symbol.bytes()); @@ -589,19 +575,18 @@ void putAutomaton() throws CharacterCodingException { } else if (chain.isParameterizedEffector()) { transitionMatrix[inputOrdinal][rteState][1] = Transducer.action(-1 * effectVector[0], effectVector[1]); } else { - Ints vector = new Ints(effectVector); - Integer vectorOrdinal = this.effectorVectorMap.get(vector); - if (vectorOrdinal == null) { - vectorOrdinal = this.effectorVectorMap.size(); - this.effectorVectorMap.put(vector, vectorOrdinal); - } - transitionMatrix[inputOrdinal][rteState][1] = -vectorOrdinal; + Integer vectorOrdinal = this.effectorVectorMap.computeIfAbsent( + new Ints(effectVector), absent -> this.effectorVectorMap.size()); + transitionMatrix[inputOrdinal][rteState][1] = -1 * vectorOrdinal; } } } catch (CompilationException e) { this.addError(String.format("%1$s: %2$s", this.getTransducerName(), e.getMessage())); } + } else if (t.tape != 0) { + this.addError(String.format(ModelCompiler.AMBIGUOUS_STATE_MESSAGE, + this.getTransducerName(), t.from)); } } } @@ -985,8 +970,8 @@ private int[][] instrumentSumVectors(int msumOrdinal, int[][] effectVectors, int || (effectVectors[-1 * vectorOrdinal][effectVectors[-1 * vectorOrdinal].length - vectorLength] != msumOrdinal)) { int[] effect = msumEffects[cell[0]]; int[] vector = vectorOrdinal > 0 - ? (vectorOrdinal > 1 ? new int[] { vectorOrdinal, 0 } : new int[] { vectorOrdinal }) - : effectVectors[-1 * vectorOrdinal]; + ? (vectorOrdinal > 1 ? new int[] { vectorOrdinal, 0 } : new int[] { vectorOrdinal }) + : effectVectors[-1 * vectorOrdinal]; int[] vectorex = Arrays.copyOf(vector, vector.length + effect.length - 1); System.arraycopy(effect, 0, vectorex, vector.length - 1, effect.length); Ints vxkey = new Ints(vectorex); diff --git a/src/com/characterforming/ribose/IParameterizedEffector.java b/src/com/characterforming/ribose/IParameterizedEffector.java index 6260c8e..0058262 100644 --- a/src/com/characterforming/ribose/IParameterizedEffector.java +++ b/src/com/characterforming/ribose/IParameterizedEffector.java @@ -81,11 +81,12 @@ * P[] precompiled by the respective proxy effector when the model * is loaded into the runtime. Live effectors are not otherwise involved * in parameter compilation or decompilation and are never passivated. They - * select their parameters by array index in their {@link #invoke(int p)} - * methods, which normally return {@link IEffector#RTX_NONE} to indicate - * no special condition. They may also return a {@link Signal} encoded by - * {@link IOutput#signal(int)}. See the javadoc comments for {@link - * IEffector} for more information regarding effector {@code RTX} codes. + * select their parameters by array index by calling {@link #getParameter(int p)} + * in their {@link #invoke(int p)} methods, which normally return {@link + * IEffector#RTX_NONE} to indicate no special condition. They may also return + * a {@link Signal} encoded by {@link IOutput#signal(int)}. See the javadoc + * comments for {@link IEffector} for more information regarding effector + * {@code RTX} codes. *

* All {@code IParameterizedEffector} implementations must be subclasses of * {@link BaseParameterizedEffector}. Other than immutability ribose places diff --git a/src/com/characterforming/ribose/base/BaseParameterizedEffector.java b/src/com/characterforming/ribose/base/BaseParameterizedEffector.java index ca0eb5b..dd31240 100644 --- a/src/com/characterforming/ribose/base/BaseParameterizedEffector.java +++ b/src/com/characterforming/ribose/base/BaseParameterizedEffector.java @@ -37,11 +37,13 @@ *
  • {@link IParameterizedEffector#allocateParameters(int)}
  • *
  • {@link IParameterizedEffector#compileParameter(IToken[])}
  • * - * Default {@link showParameterType()} and {@link showParameterTokens(int)} - * methods are implemented here but subclasses may override these if desired. - * Otherwise, public methods not exposed in the {@link IParameterizedEffector} interface - * are for internal use only. These methods implement the parameter compilation and binding - * protocols for all parameterized effector implementations. + * Subclasses may access indexed compiled parameter objects in their {@link #invoke(int)} + * implementations using {@link #getParameter(int)}. Default {@link showParameterType()} + * and {@link showParameterTokens(int)} methods are implemented here but subclasses + * may override these if desired. Otherwise, public methods not exposed in the + * {@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[])) @@ -50,7 +52,7 @@ */ public abstract class BaseParameterizedEffector extends BaseEffector implements IParameterizedEffector { - /** Effector parameters indexed and selected by parameter ordinal.*/ + /** Raw and compiled effector parameters indexed and selected by parameter ordinal.*/ private IToken[][] tokens = null; private P[] parameters = null; diff --git a/src/com/characterforming/ribose/base/Codec.java b/src/com/characterforming/ribose/base/Codec.java index 24df002..e0cb09a 100644 --- a/src/com/characterforming/ribose/base/Codec.java +++ b/src/com/characterforming/ribose/base/Codec.java @@ -34,7 +34,8 @@ * Thread local charset encoder/decoder. This is attached to a ribose thread on first use * and detached when the thread closes a model (directly or implicitly via autoclose) * or explicitly calls {@link IModel#detach()}. Static methods for encoding and decoding - * are provided. + * are provided. Codecs are sticky, if any codec method is invoked after {@link #detach()} + * or {@link IModel#close()} a new Codec will be attached (and require detaching). */ public class Codec { private static final Charset CHARSET = Charset.forName(System.getProperty("ribose.runtime.charset", "UTF-8")); @@ -56,14 +57,20 @@ private Codec() { } private static Codec set() { - Codec codec = new Codec(); - Codec.LOCAL.set(codec); + Codec codec = Codec.LOCAL.get(); + if (codec == null) { + codec = new Codec(); + Codec.LOCAL.set(codec); + } return codec; } private static Codec get() { - Codec codec = Codec.LOCAL.get() ; - return (codec == null) ? Codec.set() : codec; + Codec codec = Codec.LOCAL.get(); + if (codec == null) { + codec = Codec.set(); + } + return codec; } /** @@ -74,38 +81,41 @@ public static void detach() { } /** - * Decode UTF-8 bytes to a String. + * Decode some UTF-8 bytes to a String. * * @param bytes the bytes to decode - * @param length the number of bytes to decode + * @param length the number of bytes to decode from offset 0 * @return a String containing the decoded text * @throws CharacterCodingException if decoding fails */ - public static String decode(final byte[] bytes, final int length) throws CharacterCodingException { + public static String decode(final byte[] bytes, final int length) + throws CharacterCodingException { assert 0 <= length && length <= bytes.length; int size = Math.max(Math.min(length, bytes.length), 0); return Codec.get().decoder.reset().decode(ByteBuffer.wrap(bytes, 0, size)).toString(); } /** - * Decode UTF-8 bytes to a String. + * Decode all UTF-8 bytes to a String. * * @param bytes the bytes to decode * @return a String containing the decoded text * @throws CharacterCodingException if decoding fails */ - public static String decode(final byte[] bytes) throws CharacterCodingException { + public static String decode(final byte[] bytes) + throws CharacterCodingException { return Codec.decode(bytes, bytes.length); } /** - * Encode a String. + * Encode a String to UTF-8. * * @param chars the string to encode * @return the encoded Bytes * @throws CharacterCodingException if encoding fails */ - public static Bytes encode(final String chars) throws CharacterCodingException { + public static Bytes encode(final String chars) + throws CharacterCodingException { ByteBuffer buffer = Codec.get().encoder.reset().encode(CharBuffer.wrap(chars.toCharArray())); byte[] bytes = new byte[buffer.limit()]; buffer.get(bytes, 0, bytes.length);