Skip to content

Commit

Permalink
taf cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
mtf90 committed Aug 5, 2024
1 parent 5b9f3c9 commit 34e6a5d
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Fixed a bug in `SBAs#toCFMPS` which would allow the returned view to reach a final node on a non-return symbol.
* Fixed (another) inconsistency bug in `Incremental*DAGBuilder`s.
* The `AUTParser` now correctly reads non-deterministic automata.
* The `TAFParsers` now correctly support wildcard transition definitions for DFAs.


## [0.11.0](https://github.com/LearnLib/automatalib/releases/tag/automatalib-0.11.0) - 2023-11-06
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ public void init(Alphabet<String> alphabet) {
@Override
public void declareState(String identifier, Set<String> options) {
if (!declaredStates.add(identifier)) {
error("State {0} declared twice", identifier);
error("State {0} declared twice, ignoring properties ...", identifier);
return;
}

boolean init = options.remove("initial") | options.remove("init");
if (init && automaton.getInitialState() != null) {
error("Duplicate initial state {0}", identifier);
error("Duplicate initial state {0}, ignoring property ...", identifier);
init = false;
}

Expand All @@ -85,7 +86,9 @@ public void declareState(String identifier, Set<String> options) {

@Override
public M finish() {
checkState();
if (automaton == null) {
throw new IllegalStateException("Must call one of the parse methods first");
}

stateMap.clear();
declaredStates.clear();
Expand All @@ -95,12 +98,6 @@ public M finish() {
return result;
}

protected void checkState() {
if (automaton == null) {
throw new IllegalStateException();
}
}

protected void error(String msgFmt, Object... args) {
parser.error(msgFmt, args);
}
Expand All @@ -125,11 +122,12 @@ protected void doAddTransitions(String source, Collection<String> symbols, Strin
T exTrans = automaton.getTransition(src, input);
if (exTrans != null) {
if (!Objects.equals(tgt, automaton.getSuccessor(exTrans))) {
error("Duplicate transition from {0} on input {1} to differing target {2}" +
" would introduce non-determinism", source, StringUtil.enquoteIfNecessary(input, ID_PATTERN), tgt);
error("Duplicate transition from {0} on input {1} to differing target {2} would introduce non-determinism",
source,
StringUtil.enquoteIfNecessary(input, ID_PATTERN),
tgt);
} else if (!Objects.equals(transProperty, automaton.getTransitionProperty(exTrans))) {
error("Duplicate transition from {0} on input {1} to {2} with " +
"differing property '{3}' would introduce non-determinism",
error("Duplicate transition from {0} on input {1} to {2} with differing property '{3}' would introduce non-determinism",
source,
StringUtil.enquoteIfNecessary(input, ID_PATTERN),
tgt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
import net.automatalib.automaton.transducer.impl.CompactMealy;
import net.automatalib.common.util.IOUtil;
import net.automatalib.exception.FormatException;
import net.automatalib.serialization.ModelDeserializer;
import net.automatalib.serialization.InputModelData;
import net.automatalib.serialization.InputModelDeserializer;

final class TAFAnyParser implements ModelDeserializer<FiniteAlphabetAutomaton<?, String, ?>> {
final class TAFAnyParser implements InputModelDeserializer<String, FiniteAlphabetAutomaton<?, String, ?>> {

@Override
public FiniteAlphabetAutomaton<?, String, ?> readModel(InputStream is) throws IOException, FormatException {
public InputModelData<String, FiniteAlphabetAutomaton<?, String, ?>> readModel(InputStream is) throws IOException, FormatException {

try (Reader r = IOUtil.asNonClosingUTF8Reader(is)) {
final InternalTAFParser parser = new InternalTAFParser(r);
Expand All @@ -42,13 +43,13 @@ final class TAFAnyParser implements ModelDeserializer<FiniteAlphabetAutomaton<?,
final DefaultTAFBuilderDFA<CompactDFA<String>, Integer> builder =
new DefaultTAFBuilderDFA<>(parser, new CompactDFA.Creator<>());
parser.dfaBody(builder);
return builder.finish();
return new InputModelData<>(builder.finish(), builder.getAlphabet());
}
case MEALY: {
final DefaultTAFBuilderMealy<CompactMealy<String, String>, Integer, CompactTransition<String>>
builder = new DefaultTAFBuilderMealy<>(parser, new CompactMealy.Creator<>());
parser.mealyBody(builder);
return builder.finish();
return new InputModelData<>(builder.finish(), builder.getAlphabet());
}
default:
throw new IllegalStateException("Unknown type " + type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.InputStream;
import java.io.Reader;

import net.automatalib.alphabet.Alphabet;
import net.automatalib.automaton.AutomatonCreator;
import net.automatalib.automaton.fsa.MutableDFA;
import net.automatalib.common.util.IOUtil;
Expand Down Expand Up @@ -47,7 +48,8 @@ public InputModelData<String, A> readModel(InputStream is) throws IOException, F
throw new FormatException(ex);
}

return new InputModelData<>(builder.finish(), builder.getAlphabet());
final Alphabet<String> alphabet = builder.getAlphabet(); // finish() will clear the variable
return new InputModelData<>(builder.finish(), alphabet);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.InputStream;
import java.io.Reader;

import net.automatalib.alphabet.Alphabet;
import net.automatalib.automaton.AutomatonCreator;
import net.automatalib.automaton.transducer.MutableMealyMachine;
import net.automatalib.common.util.IOUtil;
Expand Down Expand Up @@ -48,7 +49,8 @@ public InputModelData<String, A> readModel(InputStream is) throws IOException, F
throw new FormatException(ex);
}

return new InputModelData<>(builder.finish(), builder.getAlphabet());
final Alphabet<String> alphabet = builder.getAlphabet(); // finish() will clear the variable
return new InputModelData<>(builder.finish(), alphabet);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import net.automatalib.automaton.transducer.MutableMealyMachine;
import net.automatalib.automaton.transducer.impl.CompactMealy;
import net.automatalib.serialization.InputModelDeserializer;
import net.automatalib.serialization.ModelDeserializer;

/**
* Facade for TAF (textual automaton format) parsing. This class provides several static methods to access
Expand Down Expand Up @@ -106,7 +105,7 @@ public static <S, T, A extends MutableMealyMachine<S, String, T, String>> InputM
* @see #dfa()
* @see #mealy()
*/
public static ModelDeserializer<FiniteAlphabetAutomaton<?, String, ?>> any() {
public static InputModelDeserializer<String, FiniteAlphabetAutomaton<?, String, ?>> any() {
return new TAFAnyParser();
}
}
3 changes: 2 additions & 1 deletion serialization/taf/src/main/javacc/TAF.jj
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ Set<String> stateOpts():
{
[
<LBRACK>
s=identifier() { result = new HashSet<String>(); result.add(s); }
s=identifier() { result = new HashSet<>(); result.add(s); }
(<COMMA> s=identifier() { result.add(s); })*
<RBRACK>
]
Expand Down Expand Up @@ -269,6 +269,7 @@ void transBlockDfa(TAFBuilderDFA builder, String source):
{
<LBRACE>
( transDeclDfa(builder, source) )*
[ wildcardTransDeclDfa(builder, source) ]
<RBRACE>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,25 @@

import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.impl.Alphabets;
import net.automatalib.automaton.Automaton;
import net.automatalib.automaton.FiniteAlphabetAutomaton;
import net.automatalib.automaton.MutableDeterministic;
import net.automatalib.automaton.UniversalAutomaton;
import net.automatalib.automaton.fsa.DFA;
import net.automatalib.automaton.fsa.impl.CompactDFA;
import net.automatalib.automaton.impl.CompactTransition;
import net.automatalib.automaton.transducer.MealyMachine;
import net.automatalib.automaton.transducer.impl.CompactMealy;
import net.automatalib.common.util.io.UnclosableInputStream;
import net.automatalib.common.util.io.UnclosableOutputStream;
import net.automatalib.exception.FormatException;
import net.automatalib.serialization.InputModelData;
import net.automatalib.serialization.InputModelDeserializer;
import net.automatalib.serialization.InputModelSerializer;
import net.automatalib.serialization.taf.parser.TAFParsers;
import net.automatalib.serialization.taf.writer.TAFWriters;
import net.automatalib.util.automaton.Automata;
import net.automatalib.util.automaton.random.RandomAutomata;
import net.automatalib.word.Word;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -83,6 +87,81 @@ public void testMealySerialization() throws IOException, FormatException {
Assert.assertTrue(Automata.testEquivalence(this.mealy, deserializedModel, INPUT_ALPHABET));
}

@Test
public void testAnySerializationDFA() throws IOException, FormatException {
final InputModelDeserializer<String, FiniteAlphabetAutomaton<?, String, ?>> deserializer = TAFParsers.any();
final InputModelSerializer<String, FiniteAlphabetAutomaton<Integer, String, Integer>> serializer =
TAFWriters.any();
final FiniteAlphabetAutomaton<?, String, ?> deserializedModel =
writeAndReadModel(this.dfa, INPUT_ALPHABET, serializer, deserializer);

Assert.assertNotNull(deserializedModel);
}

@Test
public void testAnySerializationMealy() throws IOException, FormatException {
final InputModelDeserializer<String, FiniteAlphabetAutomaton<?, String, ?>> deserializer = TAFParsers.any();
final InputModelSerializer<String, FiniteAlphabetAutomaton<Integer, String, CompactTransition<String>>>
serializer = TAFWriters.any();
final FiniteAlphabetAutomaton<?, String, ?> deserializedModel =
writeAndReadModel(this.mealy, INPUT_ALPHABET, serializer, deserializer);

Assert.assertNotNull(deserializedModel);
}

@Test
public void testParseDFA() throws IOException, FormatException {

final InputModelDeserializer<String, CompactDFA<String>> parser = TAFParsers.dfa();
final Alphabet<String> alphabet;
final CompactDFA<String> dfa;

try (InputStream is = TAFSerializationTest.class.getResourceAsStream("/dfa.taf")) {
final InputModelData<String, CompactDFA<String>> model = parser.readModel(is);
alphabet = model.alphabet;
dfa = model.model;
}

Assert.assertNotNull(alphabet);
Assert.assertNotNull(dfa);

Assert.assertEquals(alphabet, Alphabets.closedCharStringRange('a', 'd'));
Assert.assertTrue(dfa.accepts(Word.fromSymbols("a", "b", "c")));
Assert.assertTrue(dfa.accepts(Word.fromSymbols("a", "b", "c", "a", "b", "c")));
Assert.assertFalse(dfa.accepts(Word.fromSymbols("a", "a", "b")));
}

@Test
public void testParseMealy() throws IOException, FormatException {

final InputModelDeserializer<String, CompactMealy<String, String>> parser = TAFParsers.mealy();
final Alphabet<String> alphabet;
final CompactMealy<String, String> mealy;

try (InputStream is = TAFSerializationTest.class.getResourceAsStream("/mealy.taf")) {
final InputModelData<String, CompactMealy<String, String>> model = parser.readModel(is);
alphabet = model.alphabet;
mealy = model.model;
}

Assert.assertNotNull(alphabet);
Assert.assertNotNull(mealy);

Assert.assertEquals(alphabet, Alphabets.fromArray("Hello", "?"));
Assert.assertEquals(mealy.computeOutput(Word.fromSymbols("Hello", "?")), Word.fromSymbols("World", "!"));
Assert.assertEquals(mealy.computeOutput(Word.fromSymbols("?", "?")), Word.fromSymbols("err", "err"));
Assert.assertNull(mealy.getState(Word.fromSymbols("Hello", "Hello")));
}

@Test
public void testParseError() throws IOException {
try (InputStream is = TAFSerializationTest.class.getResourceAsStream("/error.taf")) {
Assert.assertThrows(FormatException.class, () -> TAFParsers.dfa().readModel(is));
Assert.assertThrows(FormatException.class, () -> TAFParsers.mealy().readModel(is));
Assert.assertThrows(FormatException.class, () -> TAFParsers.any().readModel(is));
}
}

@Test
public void doNotCloseInputOutputStreamDFATest() throws IOException, FormatException {
final InputModelDeserializer<String, CompactDFA<String>> deserializer = TAFParsers.dfa();
Expand Down Expand Up @@ -110,11 +189,11 @@ public void doNotCloseInputOutputStreamMealyTest() throws IOException, FormatExc
}
}

private <I, IN extends UniversalAutomaton<?, I, ?, ?, ?>, OUT extends UniversalAutomaton<?, I, ?, ?, ?>> OUT writeAndReadModel(
IN source,
Alphabet<I> alphabet,
InputModelSerializer<I, IN> serializer,
InputModelDeserializer<I, OUT> deserializer) throws IOException, FormatException {
private <I, IN extends Automaton<?, I, ?>, OUT extends Automaton<?, I, ?>> OUT writeAndReadModel(IN source,
Alphabet<I> alphabet,
InputModelSerializer<I, IN> serializer,
InputModelDeserializer<I, OUT> deserializer)
throws IOException, FormatException {

final ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.writeModel(baos, source, alphabet);
Expand All @@ -123,11 +202,11 @@ public void doNotCloseInputOutputStreamMealyTest() throws IOException, FormatExc
return deserializer.readModel(is).model;
}

private <I, IN extends UniversalAutomaton<?, I, ?, ?, ?>, OUT extends UniversalAutomaton<?, I, ?, ?, ?>> OUT writeAndReadUnclosableModel(
IN source,
Alphabet<I> alphabet,
InputModelSerializer<I, IN> serializer,
InputModelDeserializer<I, OUT> deserializer) throws IOException, FormatException {
private <I, IN extends Automaton<?, I, ?>, OUT extends Automaton<?, I, ?>> OUT writeAndReadUnclosableModel(IN source,
Alphabet<I> alphabet,
InputModelSerializer<I, IN> serializer,
InputModelDeserializer<I, OUT> deserializer)
throws IOException, FormatException {

final ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.writeModel(new UnclosableOutputStream(baos), source, alphabet);
Expand Down
15 changes: 15 additions & 0 deletions serialization/taf/src/test/resources/dfa.taf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
dfa [a..d] {
init [initial, accepting] {
a -> s1
* -> sink
}
s1 [initial] { // duplicate initial state, first definition should win
b -> acc
}
acc [accepting] { // duplicate state, first definition should win
* -> init
}
acc {
* -> s1 // duplicate transition, first definition should win
}
}
2 changes: 2 additions & 0 deletions serialization/taf/src/test/resources/error.taf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
moore [a..b] {
init [initial] {}
15 changes: 15 additions & 0 deletions serialization/taf/src/test/resources/mealy.taf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mealy {"Hello","?"} {
init [initial] {
"Hello" / "World" -> s1
* / "err" -> sink
}
s1 {
"?" / "!" -> sink
"?" / "!" -> init // duplicate transition, first definition should win
"?" / "?" -> sink // duplicate output, first definition should win
"asd" / "?!?" -> sink // unknown input symbol
}
sink [sink] { // unknown property, should be ignored
* / "err" -> sink
}
}

0 comments on commit 34e6a5d

Please sign in to comment.