Skip to content

Commit e3ab6bb

Browse files
committed
Refactor: Centralize epsilon and blank symbol handling with SymbolConstants and update related classes for consistency
1 parent 3d80057 commit e3ab6bb

File tree

10 files changed

+179
-35
lines changed

10 files changed

+179
-35
lines changed

src/main/java/ContextFreeGrammar/CFG.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import common.Symbol;
2323
import common.ValidationMessage;
2424

25+
import static common.SymbolConstants.*;
26+
2527
/**
2628
* Represents a Context-Free Grammar (CFG) with variables, terminals, productions, and a start symbol.
2729
* This class provides functionality to validate the grammar, manipulate productions, format output,
@@ -353,7 +355,7 @@ private static void parseProduction(String line, Set<NonTerminal> variables,
353355
for (String alternative : alternatives) {
354356
alternative = alternative.trim();
355357

356-
if (alternative.equals("eps")) {
358+
if (isEpsilonInput(alternative)) {
357359
productions.add(new Production(left, Collections.emptyList()));
358360
continue;
359361
}
@@ -362,7 +364,7 @@ private static void parseProduction(String line, Set<NonTerminal> variables,
362364
if (!alternative.isEmpty()) {
363365
String[] symbols = alternative.split("\\s+");
364366
for (String symbol : symbols) {
365-
if (symbol.equals("eps")) {
367+
if (isEpsilonInput(symbol)) {
366368
throw new GrammarParseException("Epsilon 'eps' must appear alone on the right-hand side");
367369
}
368370
Symbol s = findSymbol(symbol, variables, terminals);
@@ -923,7 +925,7 @@ public List<ValidationMessage> validate() {
923925
for (Symbol symbol : p.getRight()) {
924926
if (symbol instanceof NonTerminal && !varNames.contains(symbol.getName())) {
925927
messages.add(new ValidationMessage("Production uses undefined variable: " + symbol.getName(), 0, ValidationMessage.ValidationMessageType.ERROR));
926-
} else if (symbol instanceof Terminal && !termNames.contains(symbol.getName()) && !symbol.getName().equals("eps")) {
928+
} else if (symbol instanceof Terminal && !termNames.contains(symbol.getName()) && !isEpsilonInput(symbol.getName())) {
927929
messages.add(new ValidationMessage("Production uses undefined terminal: " + symbol.getName(), 0, ValidationMessage.ValidationMessageType.WARNING));
928930
}
929931
}
@@ -1044,7 +1046,7 @@ public boolean validateGrammar() {
10441046
symbol.getName());
10451047
return false;
10461048
} else if (symbol instanceof Terminal &&
1047-
!termNames.contains(symbol.getName()) && !symbol.getName().equals("eps")) {
1049+
!termNames.contains(symbol.getName()) && !isEpsilonInput(symbol.getName())) {
10481050
System.err.println("Error: Production uses undefined terminal: " +
10491051
symbol.getName());
10501052
return false;
@@ -1134,7 +1136,7 @@ public void prettyPrint() {
11341136
.map(Symbol::getName)
11351137
.collect(Collectors.joining(" "));
11361138

1137-
sb.append(rightSide.isEmpty() ? "eps" : rightSide);
1139+
sb.append(rightSide.isEmpty() ? EPSILON_INPUT : rightSide);
11381140
}
11391141

11401142
System.out.println(sb);

src/main/java/ContextFreeGrammar/Terminal.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import common.Symbol;
44

5+
import static common.SymbolConstants.*;
6+
57
/**
68
* Represents a terminal symbol in a context-free grammar.
79
* Terminal symbols are the basic symbols from which strings are formed.
@@ -21,12 +23,12 @@ public Terminal(char value) {
2123

2224
@Override
2325
public boolean isEpsilon() {
24-
return name.equals("eps") || getValue() == 'e';
26+
return isEpsilonInput(name);
2527
}
2628

2729
@Override
2830
public String prettyPrint() {
29-
return "Terminal: " + (isEpsilon() ? "eps" : name);
31+
return "Terminal: " + (isEpsilon() ? EPSILON_INPUT : name);
3032
}
3133

3234
@Override
@@ -49,6 +51,6 @@ public int hashCode() {
4951

5052
@Override
5153
public String toString() {
52-
return isEpsilon() ? "eps" : name;
54+
return isEpsilon() ? EPSILON_INPUT : name;
5355
}
5456
}

src/main/java/DeterministicFiniteAutomaton/DFA.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import common.Symbol;
2626
import common.ValidationMessage;
2727

28+
import static common.SymbolConstants.*;
29+
2830
/**
2931
* Represents a Deterministic Finite Automaton (DFA).
3032
* This class provides functionality to parse, validate, and execute DFAs,
@@ -542,8 +544,8 @@ private State validateState(String name,
542544
messages.add(new ValidationMessage("State '" + name + "' is not defined in 'states'.", line, ValidationMessage.ValidationMessageType.ERROR));
543545
}
544546

545-
if (name.equalsIgnoreCase("eps")) {
546-
messages.add(new ValidationMessage("The state name 'eps' is reserved and cannot be used.", line, ValidationMessage.ValidationMessageType.ERROR));
547+
if (isEpsilonInput(name)) {
548+
messages.add(new ValidationMessage("The state name '" + EPSILON_INPUT + "' is reserved and cannot be used.", line, ValidationMessage.ValidationMessageType.ERROR));
547549
}
548550

549551
return state;

src/main/java/NondeterministicFiniteAutomaton/NFA.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import common.Symbol;
1111
import common.ValidationMessage;
1212
import common.ValidationMessage.ValidationMessageType;
13+
14+
import static common.SymbolConstants.*;
15+
1316
import java.util.*;
1417
import java.util.regex.Matcher;
1518
import java.util.regex.Pattern;
@@ -531,12 +534,12 @@ private List<ValidationMessage> handleTransitionLines(String line, int lineNo){
531534
List<FSATransition> transitionList = new ArrayList<>();
532535
for (String symbol : symbols) {
533536
Symbol symbolTemp;
534-
if (symbol.equals("eps")) {
535-
symbolTemp = new Symbol('_');
537+
if (isEpsilonInput(symbol)) {
538+
symbolTemp = new Symbol(EPSILON_CHAR);
536539
}else{
537540
symbolTemp = new Symbol(symbol.charAt(0));
538541
}
539-
if (!this.alphabet.contains(symbolTemp) && !symbol.equals("eps")) {
542+
if (!this.alphabet.contains(symbolTemp) && !isEpsilonInput(symbol)) {
540543
warnings.add(new ValidationMessage("Alphabet does not contain transition symbol: " + symbol,
541544
lineNo, ValidationMessageType.ERROR));
542545
continue;

src/main/java/PushDownAutomaton/PDA.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import common.ValidationMessage;
1111
import common.ValidationMessage.ValidationMessageType;
1212

13+
import static common.SymbolConstants.*;
14+
1315
import java.util.*;
1416

1517
/**
@@ -218,7 +220,7 @@ public ExecutionResult execute(String inputText) {
218220
}
219221

220222
String push = t.getStackPush();
221-
if (!"eps".equals(push)) {
223+
if (!isEpsilonInput(push)) {
222224
// push sequence to left (top at index 0)
223225
newStack = push + newStack;
224226
}
@@ -285,7 +287,7 @@ private static final class Step {
285287
}
286288

287289
private static String symToStr(Symbol s) {
288-
return (s == null || s.isEpsilon()) ? "eps" : Character.toString(s.getValue());
290+
return (s == null || s.isEpsilon()) ? EPSILON_INPUT : Character.toString(s.getValue());
289291
}
290292

291293
/** Reconstruct a human-readable transition trace from parents map. */
@@ -355,8 +357,8 @@ public String toDotCode(String inputText) {
355357

356358
for (int i = 0; i < group.size(); i++) {
357359
PDATransition t = group.get(i);
358-
String input = t.getInputSymbol().isEpsilon() ? "eps" : t.getInputSymbol().toString();
359-
String pop = t.getStackPop().isEpsilon() ? "eps" : t.getStackPop().toString();
360+
String input = t.getInputSymbol().isEpsilon() ? EPSILON_INPUT : t.getInputSymbol().toString();
361+
String pop = t.getStackPop().isEpsilon() ? EPSILON_INPUT : t.getStackPop().toString();
360362
String label = String.format("%s, %s / %s", input, pop, t.getStackPush());
361363

362364
if (isSelfLoop) {
@@ -458,12 +460,12 @@ private Symbol processStackStartSymbol(List<String> lines, int lineNum,
458460
Set<Symbol> stackAlphabet, List<ValidationMessage> messages) {
459461
if (lines == null || lines.isEmpty()) {
460462
// Allow missing stack_start: stack begins empty (epsilon)
461-
return new Symbol('_');
463+
return new Symbol(EPSILON_CHAR);
462464
}
463465
String stackStartStr = lines.get(0).trim();
464-
if ("eps".equals(stackStartStr)) {
466+
if (isEpsilonInput(stackStartStr)) {
465467
// Explicit epsilon → stack begins empty
466-
return new Symbol('_');
468+
return new Symbol(EPSILON_CHAR);
467469
}
468470
if (stackStartStr.length() != 1) {
469471
messages.add(new ValidationMessage("Stack start symbol must be a single character.", lineNum, ValidationMessageType.ERROR));
@@ -553,7 +555,7 @@ private State validateState(String name, Map<String, State> stateMap, int line,
553555
}
554556

555557
private Symbol validateInputSymbol(String s, Set<Symbol> alphabet, int line, List<ValidationMessage> messages) {
556-
if (s.equals("eps")) return new Symbol('_');
558+
if (isEpsilonInput(s)) return new Symbol(EPSILON_CHAR);
557559
if (s.length() != 1) {
558560
messages.add(new ValidationMessage("Input symbol '" + s + "' must be a single character or 'eps'.", line, ValidationMessageType.ERROR));
559561
return null;
@@ -567,7 +569,7 @@ private Symbol validateInputSymbol(String s, Set<Symbol> alphabet, int line, Lis
567569
}
568570

569571
private Symbol validateStackSymbol(String s, Set<Symbol> alphabet, int line, List<ValidationMessage> messages) {
570-
if (s.equals("eps")) return new Symbol('_');
572+
if (isEpsilonInput(s)) return new Symbol(EPSILON_CHAR);
571573
if (s.length() != 1) {
572574
messages.add(new ValidationMessage("Stack symbol '" + s + "' must be a single character or 'eps'.", line, ValidationMessageType.ERROR));
573575
return null;
@@ -581,7 +583,7 @@ private Symbol validateStackSymbol(String s, Set<Symbol> alphabet, int line, Lis
581583
}
582584

583585
private boolean validatePushString(String pushString, Set<Symbol> alphabet, int line, List<ValidationMessage> messages) {
584-
if (!"eps".equals(pushString)) {
586+
if (!isEpsilonInput(pushString)) {
585587
if (pushString.length() != 1) {
586588
messages.add(new ValidationMessage(
587589
"Stack push '" + pushString + "' must be a single character or 'eps'.",

src/main/java/PushDownAutomaton/PDATransition.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import common.State;
55
import common.Symbol;
66

7+
import static common.SymbolConstants.*;
8+
79
import java.util.Objects;
810

911
/**
@@ -113,14 +115,14 @@ public boolean popsFromStack() {
113115
* @return true if this transition pushes symbols
114116
*/
115117
public boolean pushesToStack() {
116-
return stackPush != null && !stackPush.isEmpty() && !stackPush.equals("_");
118+
return stackPush != null && !stackPush.isEmpty() && !isEpsilonInput(stackPush);
117119
}
118120

119121
@Override
120122
protected String getLabel() {
121-
String input = inputSymbol.isEpsilon() ? "ε" : inputSymbol.toString();
122-
String pop = stackPop.isEpsilon() ? "ε" : stackPop.toString();
123-
String push = (stackPush == null || stackPush.isEmpty() || stackPush.equals("_")) ? "ε" : stackPush;
123+
String input = inputSymbol.isEpsilon() ? EPSILON_DISPLAY : inputSymbol.toString();
124+
String pop = stackPop.isEpsilon() ? EPSILON_DISPLAY : stackPop.toString();
125+
String push = isNoPush(stackPush) ? EPSILON_DISPLAY : stackPush;
124126
return String.format("%s, %s/%s", input, pop, push);
125127
}
126128

src/main/java/RegularExpression/RegexSyntaxTree.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static RegularExpression.RegexOperator.CONCAT;
99
import static RegularExpression.RegexOperator.OR;
1010
import static RegularExpression.RegexOperator.STAR;
11+
import static common.SymbolConstants.*;
1112

1213
/**
1314
* Internal syntax tree representation for regular expressions.
@@ -50,7 +51,7 @@ class RegexSyntaxTree {
5051
*/
5152
String sanitize(String regex) {
5253
regex = regex.replaceAll("\\s+", ""); // delete whitespace from input
53-
regex = regex.replace("eps", "ε"); // This will make things much easier
54+
regex = regex.replace(EPSILON_INPUT, EPSILON_DISPLAY); // This will make things much easier
5455
StringBuilder sanitized = new StringBuilder();
5556
int parenthesisCount = 0;
5657
for (char c : regex.toCharArray()) {
@@ -155,7 +156,7 @@ boolean match(String input) {
155156
* Utility method to check whether the alphabet has a certain char.
156157
*/
157158
boolean alphabetHas(char c) {
158-
if (c == 'ε')
159+
if (c == EPSILON_DISPLAY.charAt(0))
159160
return true;
160161
for (char ch : alphabet)
161162
if (ch == c)

src/main/java/TuringMachine/Tape.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package TuringMachine;
22

3+
import static common.SymbolConstants.*;
34

45
import java.util.ArrayList;
56
import java.util.List;
@@ -10,7 +11,6 @@
1011
public class Tape {
1112
private final List<Character> tape;
1213
private int headPosition;
13-
private static final char BLANK = '_';
1414

1515
/**
1616
* Constructs a new empty tape.
@@ -71,7 +71,7 @@ public void move(TMTransition.Direction direction) {
7171

7272
private void ensureWithinBounds() {
7373
if (headPosition >= tape.size()) {
74-
tape.add(BLANK);
74+
tape.add(BLANK_CHAR);
7575
}
7676
}
7777

src/main/java/common/Symbol.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package common;
22

3+
import static common.SymbolConstants.*;
4+
35
import java.util.Objects;
46

57
/**
@@ -42,21 +44,21 @@ public String getName() {
4244
/**
4345
* Checks if this symbol represents the epsilon (empty string) symbol.
4446
* Epsilon is represented internally by the underscore character '_'.
45-
*
47+
*
4648
* @return {@code true} if this symbol is epsilon, {@code false} otherwise
4749
*/
48-
public boolean isEpsilon() { return value == '_'; }
50+
public boolean isEpsilon() { return value == EPSILON_CHAR; }
4951

5052

5153
/**
5254
* Returns a human-readable string representation of this symbol.
5355
* For epsilon symbols, returns "Symbol: eps". For all other symbols,
5456
* returns "Symbol: " followed by the character value.
55-
*
57+
*
5658
* @return a formatted string representation of this symbol
5759
*/
5860
public String prettyPrint() {
59-
return "Symbol: " + (isEpsilon() ? "eps" : value);
61+
return "Symbol: " + (isEpsilon() ? EPSILON_INPUT : value);
6062
}
6163

6264
/**

0 commit comments

Comments
 (0)