Skip to content

Commit

Permalink
#63. Negative Lookahead Error
Browse files Browse the repository at this point in the history
First implementation
  • Loading branch information
curious-odd-man committed Aug 21, 2021
1 parent 2330901 commit b6657c6
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 9 deletions.
7 changes: 7 additions & 0 deletions pom.xml
Expand Up @@ -35,6 +35,7 @@
<maven.versions.plugin.version>2.7</maven.versions.plugin.version>

<jacoco.maven.plugin.version>0.8.5</jacoco.maven.plugin.version>
<mockito-core.version>3.12.1</mockito-core.version>
</properties>

<licenses>
Expand Down Expand Up @@ -286,5 +287,11 @@
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito-core.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
38 changes: 30 additions & 8 deletions src/main/java/com/github/curiousoddman/rgxgen/RgxGen.java
Expand Up @@ -18,15 +18,18 @@

import com.github.curiousoddman.rgxgen.config.RgxGenProperties;
import com.github.curiousoddman.rgxgen.iterators.StringIterator;
import com.github.curiousoddman.rgxgen.iterators.ValidatedIterator;
import com.github.curiousoddman.rgxgen.nodes.Node;
import com.github.curiousoddman.rgxgen.parsing.NodeTreeBuilder;
import com.github.curiousoddman.rgxgen.parsing.dflt.DefaultTreeBuilder;
import com.github.curiousoddman.rgxgen.validation.Validator;
import com.github.curiousoddman.rgxgen.visitors.GenerationVisitor;
import com.github.curiousoddman.rgxgen.visitors.NotMatchingGenerationVisitor;
import com.github.curiousoddman.rgxgen.visitors.UniqueGenerationVisitor;
import com.github.curiousoddman.rgxgen.visitors.UniqueValuesCountingVisitor;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;
Expand All @@ -37,7 +40,8 @@
public class RgxGen {
private static RgxGenProperties aGlobalProperties;

private final Node aNode;
private final Node aNode;
private final List<Validator> aValidators;

private RgxGenProperties aLocalProperties = aGlobalProperties;

Expand Down Expand Up @@ -69,6 +73,7 @@ public RgxGen(CharSequence pattern) {
*/
public RgxGen(NodeTreeBuilder builder) {
aNode = builder.get();
aValidators = builder.getValidators();
}

/**
Expand Down Expand Up @@ -97,6 +102,9 @@ public void setProperties(RgxGenProperties properties) {
*/
@Deprecated
public BigInteger numUnique() {
if (!aValidators.isEmpty()) {
return null;
}
UniqueValuesCountingVisitor v = new UniqueValuesCountingVisitor(aLocalProperties);
aNode.visit(v);
return v.getEstimation()
Expand All @@ -111,6 +119,10 @@ public BigInteger numUnique() {
* though actual count is only 5, because right and left part of group can yield same value
*/
public Optional<BigInteger> getUniqueEstimation() {
if (!aValidators.isEmpty()) {
return Optional.empty();
}

UniqueValuesCountingVisitor v = new UniqueValuesCountingVisitor(aLocalProperties);
aNode.visit(v);
return v.getEstimation();
Expand All @@ -134,7 +146,11 @@ public Stream<String> stream() {
public StringIterator iterateUnique() {
UniqueGenerationVisitor ugv = new UniqueGenerationVisitor(aLocalProperties);
aNode.visit(ugv);
return ugv.getUniqueStrings();
if (aValidators.isEmpty()) {
return ugv.getUniqueStrings();
} else {
return new ValidatedIterator(aValidators, ugv.getUniqueStrings());
}
}

/**
Expand All @@ -154,12 +170,18 @@ public String generate() {
* @return generated string.
*/
public String generate(Random random) {
GenerationVisitor gv = GenerationVisitor.builder()
.withRandom(random)
.withProperties(aLocalProperties)
.get();
aNode.visit(gv);
return gv.getString();
String[] value = new String[1];
do {
GenerationVisitor gv = GenerationVisitor.builder()
.withRandom(random)
.withProperties(aLocalProperties)
.get();
aNode.visit(gv);
value[0] = gv.getString();
} while (!aValidators.stream()
.allMatch(validator -> validator.validate(value[0])));

return value[0];
}

/**
Expand Down
Expand Up @@ -21,7 +21,7 @@
public interface StringIterator extends Iterator<String> {
/**
* Reset the iterator to the initial position.
* After reset it will start iterating from the first value.
* After reset, it will start iterating from the first value.
* <p>
* Can be used to restart iterator that returns {@code false} when {@code hasNext()} is called.
*/
Expand Down
@@ -0,0 +1,78 @@
package com.github.curiousoddman.rgxgen.iterators;

import com.github.curiousoddman.rgxgen.validation.Validator;

import java.util.List;
import java.util.NoSuchElementException;

public class ValidatedIterator implements StringIterator {
private final List<Validator> aValidatorList;
private final StringIterator aStringIterator;

private boolean isInitialized = false;
private String aCurrentValue = null;
private boolean hasNext = true;

public ValidatedIterator(List<Validator> validatorList, StringIterator stringIterator) {
aValidatorList = validatorList;
aStringIterator = stringIterator;
}

@Override
public void reset() {
isInitialized = false;
hasNext = true;
aCurrentValue = null;
aStringIterator.reset();
}

@Override
public String current() {
return aCurrentValue;
}

private boolean findNextValid() {
while (aStringIterator.hasNext()) {
String next = aStringIterator.next();
if (aValidatorList.stream()
.allMatch(v -> v.validate(next))) {
return true;
}
}

return false;
}

private void initialize() {
isInitialized = true;
boolean hasValid = findNextValid();
if (hasValid) {
aCurrentValue = aStringIterator.current();
hasNext = findNextValid();
}
}

@Override
public boolean hasNext() {
if (isInitialized) {
return hasNext;
} else {
initialize();
return aCurrentValue != null;
}
}

@Override
public String next() {
if (isInitialized) {
aCurrentValue = aStringIterator.current();
hasNext = findNextValid();
} else {
initialize();
if (aCurrentValue == null) {
throw new NoSuchElementException("No texts can be produced by this pattern.");
}
}
return aCurrentValue;
}
}
Expand Up @@ -17,6 +17,9 @@
/* **************************************************************************/

import com.github.curiousoddman.rgxgen.nodes.Node;
import com.github.curiousoddman.rgxgen.validation.Validator;

import java.util.List;

/**
* Interface for the parser/nodes builder.
Expand All @@ -27,4 +30,10 @@ public interface NodeTreeBuilder {
* @return Root node for the parsed pattern
*/
Node get();

/**
*
* @return list of validators that should be applied to determine if the generated text satisfies them all.
*/
List<Validator> getValidators();
}
Expand Up @@ -19,6 +19,7 @@
import com.github.curiousoddman.rgxgen.nodes.*;
import com.github.curiousoddman.rgxgen.parsing.NodeTreeBuilder;
import com.github.curiousoddman.rgxgen.util.Util;
import com.github.curiousoddman.rgxgen.validation.Validator;

import java.util.*;

Expand All @@ -36,6 +37,7 @@ public class DefaultTreeBuilder implements NodeTreeBuilder {

private final CharIterator aCharIterator;
private final Map<Node, Integer> aNodesStartPos = new IdentityHashMap<>();
private final List<Validator> aValidators = new ArrayList<>();

private Node aNode;
private int aNextGroupIndex = 1;
Expand Down Expand Up @@ -674,4 +676,9 @@ public Node get() {
}
return aNode;
}

@Override
public List<Validator> getValidators() {
return aValidators;
}
}
@@ -0,0 +1,32 @@
package com.github.curiousoddman.rgxgen.validation;

/* **************************************************************************
Copyright 2019 Vladislavs Varslavans
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
/* **************************************************************************/

/**
* Validators are introduced to tackle lookaround generation.
* They check if the text (or part of it) matches lookahead or lookbehind patterns.
*/
public interface Validator {

/**
* Checks if {@code text} is valid
*
* @param text text to check
* @return true if text is valid, false otherwise
*/
boolean validate(String text);
}
Expand Up @@ -238,6 +238,11 @@ public enum TestPattern implements DataInterface {
setUseFindForMatching();
}},
//-----------------------------------------------------------------------------------------------------------------------------------------
NEGATIVE_LOOKAHEAD_BEFORE_SPANS_TWO_NODES("(?!BB)[AB][AB]",
new FinalSymbol("NOT IMPLEMENTED")) {{
setUseFindForMatching();
}},
//-----------------------------------------------------------------------------------------------------------------------------------------
// FIXME: Same as "foo"
POSITIVE_LOOKBEHIND_AFTER("fo[od](?<=foo)",
new FinalSymbol("NOT IMPLEMENTED")) {{
Expand Down

0 comments on commit b6657c6

Please sign in to comment.