Skip to content

Commit

Permalink
WIP: Issue #24 value stack (1st draft)
Browse files Browse the repository at this point in the history
- refactor Values into standalone class
- fix up LinuxKernel pattern
- javadoc updates only for I*Effector
- javadoc runs quietly (build.xml)

Signed-off-by: jrte <[email protected]>
  • Loading branch information
jrte committed Oct 7, 2023
1 parent a998fff commit 857b3f8
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 114 deletions.
2 changes: 1 addition & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@

<target name="javadoc" unless="javadoc.uptodate" depends="check-javadoc">
<delete dir="javadoc" includes="**/*"/>
<javadoc executable="${jdk}/bin/javadoc" access="protected" overview="overview.html"
<javadoc executable="${jdk}/bin/javadoc" access="protected" overview="overview.html" additionalparam="-quiet"
sourcepath="src" destdir="javadoc" verbose="false" use="true" failonerror="false" failonwarning="false"
doctitle="Ribose" packagenames="com.characterforming.ribose.base,com.characterforming.ribose"
encoding="UTF-8" charset="UTF-8" docencoding="UTF-8">
Expand Down
13 changes: 4 additions & 9 deletions patterns/test/LinuxKernelLog.inr
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,10 @@ continue = clear[`~*`] select[`~timestamp`];

done = [continue] (((a0$(0,0))* (eos, nul)) @ null);

LinuxKernel = nil (
(
[continue]
(
iptables ((nl, save) | (eos, save stop))
| null? (nl | (eos, stop))
)
)*
done
LinuxKernel = (
(nil, continue)
(iptables (nl, save continue) | null? (nl, continue))*
(iptables (eos, save stop) | null? (eos, stop))
):dfamin;

### LinuxKernelStrict
Expand Down
53 changes: 9 additions & 44 deletions src/com/characterforming/jrte/engine/TransducerStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,6 @@
* @author Kim Briggs
*/
final class TransducerStack {
class Value {
private byte[] val;
private int len;

Value(int size) {
this.val = new byte[Math.max(size, 16)];
this.len = 0;
}

int length() { return this.len; }

byte[] value() { return this.val; }

void clear() { this.len = 0; }

void paste(byte input) {
if (this.len >= this.val.length) {
this.val = Arrays.copyOf(this.val, this.len + (this.len >> 1));
}
this.val[this.len] = input;
this.len += 1;
}

void paste(byte[] input, int length) {
assert length <= input.length;
int newLength = this.len + length;
if (newLength > this.val.length) {
this.val = Arrays.copyOf(this.val, newLength + (this.len >> 1));
}
System.arraycopy(input, 0, this.val, this.len, length);
this.len += length;
}

void paste(Value value) {
this.paste(value.val, value.length());
}
}

private Value[] values;
private TransducerState[] stack;
private int tos, bof;
Expand Down Expand Up @@ -188,8 +150,8 @@ boolean isEmpty() {
void clear() {
if (this.bof >= 0) {
int tof = this.bof + this.stack[tos].get().getFieldCount();
while (tof-- > this.bof) {
this.values[tof].clear();
while (tof > this.bof) {
this.values[--tof].clear();
}
}
}
Expand All @@ -200,9 +162,10 @@ void clear() {
* @param ordinal The index of the value to clear
*/
void clear(int ordinal) {
if (this.bof >= 0 && (ordinal == 0 || ordinal < this.stack[this.tos].get().getFieldCount())) {
this.values[this.bof + ordinal].clear();
}
assert this.bof >= 0 && ordinal < this.stack[this.tos].get().getFieldCount()
: String.format("TransducerStack.clear(ordinal=%1$d): bof=%2$d, %3$d fields in frame",
ordinal, this.bof, this.tos >= 0 ? this.stack[this.tos].get().getFieldCount() : 0);
this.values[this.bof + ordinal].clear();
}

/**
Expand All @@ -212,7 +175,9 @@ void clear(int ordinal) {
* @return The value
*/
Value value(int ordinal) {
assert this.bof < 0 || ordinal == 0 || ordinal < this.stack[this.tos].get().getFieldCount();
assert this.bof >= 0 && ordinal < this.stack[this.tos].get().getFieldCount()
: String.format("TransducerStack.value(ordinal=%1$d): bof=%2$d, %3$d fields in frame",
ordinal, this.bof, this.tos >= 0 ? this.stack[this.tos].get().getFieldCount() : 0);
return this.values[this.bof + ordinal];
}
}
38 changes: 16 additions & 22 deletions src/com/characterforming/jrte/engine/Transductor.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public final class Transductor implements ITransductor, IOutput {
private final ModelLoader loader;
private TransducerState transducer;
private IEffector<?>[] effectors;
private TransducerStack.Value value;
private Value value;
private Signal prologue;
private int selected;
private final TransducerStack transducerStack;
Expand Down Expand Up @@ -224,7 +224,7 @@ public int getLocalizedFieldndex() throws EffectorException {
@Override // @see com.characterforming.ribose.IOutput#asBytes(int)
public byte[] asBytes(int fieldOrdinal) throws EffectorException {
if (!this.isProxy) {
TransducerStack.Value v = this.transducerStack.value(fieldOrdinal);
Value v = this.transducerStack.value(fieldOrdinal);
return Arrays.copyOf(v.value(), v.length());
} else
throw new EffectorException("Not valid for proxy transductor");
Expand All @@ -233,7 +233,7 @@ public byte[] asBytes(int fieldOrdinal) throws EffectorException {
@Override // @see com.characterforming.ribose.IOutput#asString(int)
public String asString(int fieldOrdinal) throws EffectorException {
if (!this.isProxy) {
TransducerStack.Value v = this.transducerStack.value(fieldOrdinal);
Value v = this.transducerStack.value(fieldOrdinal);
try {
return this.decoder().decode(
ByteBuffer.wrap(v.value(), 0, v.length())).toString();
Expand All @@ -252,7 +252,7 @@ public String asString(int fieldOrdinal) throws EffectorException {
@Override // @see com.characterforming.ribose.IOutput#asInteger()
public long asInteger(int fieldOrdinal) throws EffectorException {
if (!this.isProxy) {
TransducerStack.Value v = this.transducerStack.value(fieldOrdinal);
Value v = this.transducerStack.value(fieldOrdinal);
byte[] data = v.value();
long sign = data[0] == '-' ? -1 : 1;
long n = 0;
Expand All @@ -273,7 +273,7 @@ public long asInteger(int fieldOrdinal) throws EffectorException {
@Override // @see com.characterforming.ribose.IOutput#asReal()
public double asReal(int fieldOrdinal) throws EffectorException {
if (!this.isProxy) {
TransducerStack.Value v = this.transducerStack.value(fieldOrdinal);
Value v = this.transducerStack.value(fieldOrdinal);
byte[] data = v.value();
double f = data[0] == '-' ? -1.0 : 1.0;
boolean mark = false;
Expand Down Expand Up @@ -794,7 +794,7 @@ public int invoke(final int parameterIndex) throws EffectorException {
for (IToken t : super.getParameter(parameterIndex)) {
if (t instanceof Token token) {
if (token.getType() == IToken.Type.FIELD) {
TransducerStack.Value field = transducerStack.value(token.getOrdinal());
Value field = transducerStack.value(token.getOrdinal());
if (field != null) {
value.paste(field);
}
Expand Down Expand Up @@ -850,9 +850,7 @@ public int invoke() throws EffectorException {
public int invoke(final int parameterIndex) throws EffectorException {
int fieldOrdinal = super.getParameter(parameterIndex);
assert fieldOrdinal != Model.ALL_FIELDS_ORDINAL;
if (fieldOrdinal != Model.ALL_FIELDS_ORDINAL) {
value.paste(transducerStack.value(fieldOrdinal));
}
value.paste(transducerStack.value(fieldOrdinal));
return IEffector.RTX_NONE;
}
}
Expand All @@ -871,10 +869,8 @@ public int invoke() throws EffectorException {
public int invoke(final int parameterIndex) throws EffectorException {
int fieldOrdinal = super.getParameter(parameterIndex);
assert fieldOrdinal != Model.ALL_FIELDS_ORDINAL;
if (fieldOrdinal != Model.ALL_FIELDS_ORDINAL) {
value.paste(transducerStack.value(fieldOrdinal));
transducerStack.value(fieldOrdinal).clear();
}
value.paste(transducerStack.value(fieldOrdinal));
transducerStack.value(fieldOrdinal).clear();
return IEffector.RTX_NONE;
}
}
Expand Down Expand Up @@ -928,8 +924,7 @@ public Integer[] allocateParameters(int parameterCount) {
public Integer compileParameter(final IToken[] parameterList) throws TargetBindingException {
if (parameterList.length != 1) {
throw new TargetBindingException("The signal effector accepts at most one parameter");
}
if (parameterList[0] instanceof Token token) {
} else if (parameterList[0] instanceof Token token) {
if (token.getType() == IToken.Type.SIGNAL) {
int ordinal = token.getOrdinal();
if (ordinal < 0) {
Expand Down Expand Up @@ -965,7 +960,7 @@ public int invoke(final int parameterIndex) throws EffectorException {
byte[][] tokens = new byte[parameters.length][];
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].getType() == IToken.Type.FIELD) {
TransducerStack.Value field = transducerStack.value(parameters[i].getOrdinal());
Value field = transducerStack.value(parameters[i].getOrdinal());
tokens[i] = (field != null) ? field.value() : Bytes.EMPTY_BYTES;
} else {
assert parameters[i].getType() == IToken.Type.LITERAL;
Expand Down Expand Up @@ -993,7 +988,7 @@ public int invoke(final int parameterIndex) throws EffectorException {
for (final IToken token : super.getParameter(parameterIndex)) {
try {
if (token.getType() == IToken.Type.FIELD) {
TransducerStack.Value field = transducerStack.value(token.getOrdinal());
Value field = transducerStack.value(token.getOrdinal());
if (field != null) {
outputStream.write(field.value(), 0, field.length());
}
Expand Down Expand Up @@ -1085,13 +1080,12 @@ 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");
}
if (parameterTokens[0].getType() == IToken.Type.TRANSDUCER) {
return parameterTokens[0].getOrdinal();
} else {
throw new TargetBindingException(String.format("Invalid transducer reference `%s` for start effector, requires type indicator ('%c') before the transducer name",
} else if (parameterTokens[0].getType() != IToken.Type.TRANSDUCER) {
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));
}
return parameterTokens[0].getOrdinal();
}

@Override
Expand Down
40 changes: 40 additions & 0 deletions src/com/characterforming/jrte/engine/Value.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.characterforming.jrte.engine;

import java.util.Arrays;

class Value {
private byte[] val;
private int len;

Value(int size) {
this.val = new byte[Math.max(size, 16)];
this.len = 0;
}

int length() { return this.len; }

byte[] value() { return this.val; }

void clear() { this.len = 0; }

void paste(byte input) {
if (this.len >= this.val.length) {
this.val = Arrays.copyOf(this.val, this.len + (this.len >> 1));
}
this.val[this.len++] = input;
}

void paste(byte[] input, int length) {
assert length <= input.length;
int newLength = this.len + length;
if (newLength > this.val.length) {
this.val = Arrays.copyOf(this.val, newLength + (this.len >> 1));
}
System.arraycopy(input, 0, this.val, this.len, length);
this.len = newLength;
}

void paste(Value value) {
this.paste(value.val, value.length());
}
}
17 changes: 11 additions & 6 deletions src/com/characterforming/ribose/IEffector.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,23 @@

package com.characterforming.ribose;

import com.characterforming.ribose.base.BaseEffector;
import com.characterforming.ribose.base.Bytes;
import com.characterforming.ribose.base.EffectorException;

/**
* Interface for simple effectors that present only a niladic {@link #invoke()}
* method which is called on live effector instances in response to state transitions
* in a running transduction. They are typically implemented as anonymous inner classes
* within a specialized {@link ITarget} implementation classes. In compile contexts
* simple effectors are instantiated as proxies but receive only {@link #passivate()},
* which clears its internal fields. In runtime contexts they instantiated as live
* effectors. Live simple effectors receive a {@link #setOutput(IOutput)} call followed
* by 0 or more calls to {@link #invoke()}.
* in a running transduction. Simple effector implementations must extend {@link
* BaseEffector} and implement {@link invoke()} and may override other base effector
* methods. They are typically implemented as anonymous inner classes within a specialized
* {@link ITarget} implementation classes. Proxy simple effectors, instantiated in model
* compilation and loding contexts, receive {@link #setOutput(IOutput)} and {@link #passivate()}
* but never receive {@link #invoke()}. They lose access to their {@link ITarget} instance
* when they are passivated, {@link #getTarget()} will return {@code null}. Live simple
* effectors instantiated in runtime contexts receive {@link #setOutput(IOutput)} once
* followed by 0 or more {@link #invoke()}. Live effectors are never passivated and may
* use {@link #getTarget()} in their {@link #invoke()} methods.
* <br><br>
* All effectors return an integer <a href="#field.summary">RTX</a> code, which is a bit
* map of special conditions. RTX bits are additive and accumulate as the effect vector is
Expand Down
67 changes: 35 additions & 32 deletions src/com/characterforming/ribose/IParameterizedEffector.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,37 @@

/**
* Interface for parameterized effectors extends {@link IEffector} with a monadic
* {@link #invoke(int p)} method that produces an effect using a <b>P</b> instance
* selected from an array of immutable parameter objects. Each parameterized effector
* is thus an indexed collection of {@link IEffector}. The array is populated with
* <b>P</b> instances compiled from lists of tokens bound to parameterized effector
* references in the ribose model. In ribose transducer patterns parameters are
* represented as lists of one or more backquoted tokens, eg {@code
* out[`~field` `,`]}. In ginr compiled FSTs and in ribose transducers these
* tokens are represented as arrays of raw bytes which may contain UTF-8 encoded
* text, binary data encoded using hexadecimal {@code \xHH} representation for
* unprintable bytes, or field (`~field`), transducer (`@transducer`) or
* signal (`!signal`) references.
* {@link #invoke(int p)} method that selects a <b>P</b> instance from an enumerated
* set of immutable parameter instances. Implementation classes must extend
* {@link BaseParameterizedEffector}, which implements the parameter compilation,
* binding and access protocols.
* <br><br>
* Each {@link IParameterizedEffector} implementation is a template for an indexed
* collection of {@link IEffector}. In ribose models, they are specialized by the
* union of parameter lists bound to their effectors in transducer patterns. In
* ribose transducer patterns effector parameters are represented as lists of one
* or more backquoted tokens, eg {@code out[`~field` `,`]}. In ginr compiled FSTs
* and in ribose transducers these tokens are represented as arrays of raw bytes
* which may contain UTF-8 encoded text, binary data encoded using hexadecimal
* {@code \xHH} representation for unprintable bytes, or field (`~field`),
* transducer (`@transducer`) or signal (`!signal`) references. Tokens are
* presented in the {@link IToken} interface.
* <br><br>
* <i>Proxy</i> parameterized effectors compile parameters from arrays of {@link
* IToken} when a model is compiled and when it is loaded for runtime use. Each
* token array is compiled to an immutable instance of the effector's parameter
* type <b>P</b>, which must be constructible from {@code IToken[]}. The model
* will call {@link #allocateParameters(int)} to obtain an array <b>P</b>[] of
* parameter instances and {@link #compileParameter(IToken[])} for each parameter
* to populate the array. When parameter compilation is complete the proxy effector
* is passivated with a call to {@link #passivate()} and its raw tokens and compiled
* parameters are retained for binding to live effectors. The {@link #invoke()}
* and {@link #invoke(int)} methods are never called for proxy effectors. Proxy
* effectors receive calls to {@link #showParameterTokens(CharsetDecoder, int)}
* and {@link #showParameterType()} from the model decompiler; these methods are
* implemented in {@link BaseParameterizedEffector} but may be overridden by
* subclasses.
* token array is compiled {@code IToken[] -> P} to an immutable instance of
* <b>P</b>. The model will call {@link #allocateParameters(int)} to obtain an
* array <b>P</b>[] of {@code null} and populate it by calling {@link
* #compileParameter(IToken[])} for each parameter. The proxy effector is then
* passivated while compiled and raw parameters are retained for binding to live
* effectors and decompiling transducers. Proxy efectors lose access to their
* proxy target instance after they are passivated but retain their compiled
* <b>P[]</b> instances. Proxy effectors may receive calls to {@link
* #showParameterType()} and {@link #showParameterTokens(CharsetDecoder, int)}
* from the model decompiler; these methods are implemented in {@link
* BaseParameterizedEffector} but may be overridden by subclasses. The
* {@link IEffector#invoke()} and {@link #invoke(int)} methods are never called
* for proxy effectors.
* <br><br>
* For example:
* <br><pre>
Expand All @@ -75,9 +80,8 @@
* }
* }</pre>
* <i>Live</i> parameterized effectors receive a reference to the parameters
* <b>P</b>[] precompiled by the respective proxy effector via {@link
* BaseParameterizedEffector#setParameters(IParameterizedEffector)} when the
* model is loaded into the runtime. Live effectors are not otherwise involved
* <b>P</b>[] 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
Expand All @@ -86,12 +90,11 @@
* IEffector} for more information regarding effector {@code RTX} codes.
* <br><br>
* All {@code IParameterizedEffector} implementations must be subclasses of
* {@link BaseParameterizedEffector}, which implements the parameter compilation
* protocol and provides default implementations for some of the interface. Other
* than immutability ribose places no constraints on effector implementation.
* Most effector implementations are light weight, tightly focused and single
* threaded. A parallel array of derivative objects can be allocated and
* instantiated along with the parameters array if associated data are required.
* {@link BaseParameterizedEffector}. Other than immutability ribose places
* no constraints on effector implementation. Most effector implementations
* are light weight, tightly focused and single threaded. A parallel array
* of derivative objects can be allocated and instantiated along with the
* parameters array if associated data are required.
*
* @author Kim Briggs
* @param <T> the effector target type
Expand Down

0 comments on commit 857b3f8

Please sign in to comment.