Skip to content

Commit

Permalink
FSM.Matched
Browse files Browse the repository at this point in the history
  • Loading branch information
Pastor committed Dec 18, 2024
1 parent b1850b3 commit 9e57ca8
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 120 deletions.
25 changes: 13 additions & 12 deletions vol8/src/docs/RegExpr.utext
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//https://plantuml.com/ru/ebnf
@startebnf
RE = union | simple_re;
union = RE, "|", simple_re;
simple_re = concatenation | basic_re;
concatenation = simple_re, basic_re;
regexp = or | simple_re;
or = regexp, "|", simple_re;
simple_re = add | basic_re;
add = simple_re, basic_re;
basic_re = star | plus | elementary_re;
star = elementary_re, "*";
plus = elementary_re, "+";
elementary_re = groups | "." | "$" | char | set;
groups = "(", RE, ")";
char = "characters" | "\", "characters";
set = positive_set | negative_set;
positive_set = "[", set_items, "]";
negative_set = "[^", set_items, "]";
set_items = set_item | set_item, set_items;
set_item = range | char;
range = char, "-", char;
groups = "(", regexp, ")";
char = characters | "\", characters;
set = positive_set | negative_set;
positive_set = "[", set_items, "]";
negative_set = "[^", set_items, "]";
set_items = set_item | set_item, set_items;
set_item = range | char;
range = char, "-", char;
characters = (a-z|A-Z);
@endebnf
195 changes: 195 additions & 0 deletions vol8/src/main/java/ru/mifi/practice/vol8/regexp/Mach.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package ru.mifi.practice.vol8.regexp;

import lombok.experimental.UtilityClass;
import ru.mifi.practice.vol8.regexp.visitor.MatchGenerator;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static ru.mifi.practice.vol8.regexp.Mach.Input.StringInput;

public interface Mach {

boolean match(String input);

static Mach of(State start) {
return new DefaultMach(start);
}

@UtilityClass
final class Compiler {
public Mach compile(String pattern) {
MatchGenerator generator = new MatchGenerator();
new Tree.Default(pattern).visit(generator);
return generator.getMach();
}
}

final class DefaultMach implements Mach {
private final State start;

private DefaultMach(State start) {
this.start = start;
}

@Override
public boolean match(String text) {
StringInput input = new StringInput(text);
return new Result(Match.MATCHED, start).match(input);
}
}

interface Input {

void mark();

void reset();

Optional<Character> peek();

void next();

final class StringInput implements Input {
private final char[] chars;
private int it;
private int mark;

public StringInput(String text) {
this.chars = text.toCharArray();
}

@Override
public void mark() {
mark = it;
}

@Override
public void reset() {
it = mark;
mark = 0;
}

@Override
public Optional<Character> peek() {
if (it >= chars.length) {
return Optional.empty();
}
return Optional.of(chars[it]);
}

@Override
public void next() {
if (it < chars.length) {
it++;
}
}
}
}

@SuppressWarnings("PMD.UnusedPrivateMethod")
abstract class State {
protected final Set<State> transitions = new HashSet<>();

private void add(State state) {
transitions.add(state);
}

protected abstract Match match(Input input);

protected abstract Result next(Input input);
}

final class Sequence extends State {
@Override
protected Match match(Input input) {
return Match.SKIPPED;
}

@Override
protected Result next(Input input) {
Iterator<State> it = transitions.iterator();
while (it.hasNext()) {
State state = it.next();
Result result = state.next(input);
if (result.match == Match.SKIPPED || result.match == Match.MATCHED) {
it.remove();
} else {
return new Result(Match.UNMATCHED);
}
}
return new Result(Match.MATCHED);
}
}

final class Epsilon extends State {
@Override
protected Match match(Input input) {
return Match.SKIPPED;
}

@Override
protected Result next(Input input) {
return new Result(Match.SKIPPED, transitions.toArray(new State[0]));
}
}

final class Matched extends State {
private final Character character;

private Matched(Character character) {
this.character = character;
}

@Override
protected Match match(Input input) {
return input.peek().filter(c -> c.equals(character))
.map(c -> Match.MATCHED).orElse(Match.UNMATCHED);
}

@Override
protected Result next(Input input) {
List<State> states = new ArrayList<>();
transitions.forEach(t -> {
Match match = t.match(input);
if (match == Match.MATCHED || match == Match.SKIPPED) {
states.add(t);
}
});
return new Result(states.isEmpty() ? Match.UNMATCHED : Match.MATCHED, states.toArray(new State[0]));
}
}

record Result(Match match, State... next) {
boolean match(Input input) {
Set<State> states = new HashSet<>();
for (State state : next) {
Match matched = state.match(input);
if (matched == Match.MATCHED || matched == Match.SKIPPED) {
states.add(state);
}
}
Set<Result> results = new HashSet<>();
for (State state : states) {
input.mark();
Result result = state.next(input);
input.reset();
if (result.match == Match.SKIPPED || result.match == Match.MATCHED) {
results.add(result);
}
}
return results.stream().anyMatch(r -> r.match(input));
}
}

enum Match {
MATCHED, SKIPPED, UNMATCHED
}

enum Type {
SEQUENCE, PARALLELISM
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.nio.file.Path;

@SuppressWarnings("PMD.AvoidStringBufferField")
abstract class AbstractStringVisitor implements Tree.Visitor {
abstract class AbstractStringVisitor extends AbstractVisitor {
protected final StringBuilder buffer = new StringBuilder();

public final void writeFile(String fileName) throws IOException {
Expand All @@ -24,96 +24,6 @@ public void start() {
buffer.setLength(0);
}

@Override
public void nextRange() {
//Nothing
}

@Override
public void nextSet() {
//Nothing
}

@Override
public void nextAnd() {
//Nothing
}

@Override
public void nextOr() {
//Nothing
}

@Override
public void any() {
//Nothing
}

@Override
public void end() {
//Nothing
}

@Override
public void exit(Tree.Set set) {
//Nothing
}

@Override
public void exit(Tree.Range range) {
//Nothing
}

@Override
public void exit(Tree.Group group) {
//Nothing
}

@Override
public void exit(Tree.Unary unary) {
//Nothing
}

@Override
public void exit(Tree.Or or) {
//Nothing
}

@Override
public void exit(Tree.And and) {
//Nothing
}

@Override
public void enter(Tree.Set set) {
//Nothing
}

@Override
public void enter(Tree.Range range) {
//Nothing
}

@Override
public void enter(Tree.Group group) {
//Nothing
}

@Override
public void enter(Tree.Unary unary) {
//Nothing
}

@Override
public void enter(Tree.Or or) {
//Nothing
}

@Override
public void enter(Tree.And and) {
//Nothing
}

@Override
public String toString() {
return buffer.toString();
Expand Down
Loading

0 comments on commit 9e57ca8

Please sign in to comment.