Skip to content

Commit 077a202

Browse files
Merge pull request #18317 from martin-blazevic/article/BAEL-9016
article/BAEL-9016
2 parents 8fb8319 + df43f8e commit 077a202

File tree

11 files changed

+329
-0
lines changed

11 files changed

+329
-0
lines changed

core-java-modules/core-java-23/src/main/java/com/baeldung/javafeatures/ImplicitClass.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import static java.io.IO.print;
2+
import static java.io.IO.readln;
3+
14
void main() {
25
String name = readln("Please enter your name: ");
36
print("Hello "+name);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.lexer;
2+
3+
import java.util.Optional;
4+
5+
public class Expression {
6+
7+
private final String value;
8+
private int index = 0;
9+
10+
public Expression(String value) {
11+
this.value = value != null ? value : "";
12+
}
13+
14+
public Optional<Character> next() {
15+
if (index >= value.length()) {
16+
return Optional.empty();
17+
}
18+
return Optional.of(value.charAt(index++));
19+
}
20+
21+
public boolean hasNext() {
22+
return index < value.length();
23+
}
24+
25+
public String getValue() {
26+
return value;
27+
}
28+
}
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.baeldung.lexer;
2+
3+
class InvalidExpressionException extends RuntimeException {
4+
5+
public InvalidExpressionException(String message) {
6+
super(message);
7+
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.baeldung.lexer;
2+
3+
import java.util.List;
4+
5+
public interface Lexer {
6+
7+
String MESSAGE_ERROR = "Unexpected entry: '%s'";
8+
9+
List<Token> tokenize(Expression expression);
10+
11+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.baeldung.lexer;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
import java.util.Optional;
7+
8+
public class LexerFsm implements Lexer {
9+
10+
public List<Token> tokenize(Expression expression) {
11+
State state = State.INITIAL;
12+
StringBuilder currentToken = new StringBuilder();
13+
ArrayList<Token> tokens = new ArrayList<>();
14+
15+
while (expression.hasNext()) {
16+
final Character currentChar = getValidNextCharacter(expression);
17+
18+
switch (state) {
19+
case INITIAL:
20+
if (Grammar.isWhitespace(currentChar)) {
21+
break;
22+
} else if (Grammar.isDigit(currentChar)) {
23+
state = State.NUMBER;
24+
currentToken.append(currentChar);
25+
} else if (Grammar.isOperator(currentChar) && !tokens.isEmpty()) { // to ensure there are no expressions starting with an OPERATOR
26+
state = State.OPERATOR;
27+
currentToken.append(currentChar);
28+
} else {
29+
state = State.INVALID;
30+
currentToken.append(currentChar);
31+
}
32+
break;
33+
34+
case NUMBER:
35+
if (Grammar.isDigit(currentChar)) {
36+
currentToken.append(currentChar);
37+
} else {
38+
tokens.add(new TokenNumber(currentToken.toString()));
39+
currentToken.setLength(0);
40+
state = State.INITIAL;
41+
}
42+
break;
43+
44+
case OPERATOR:
45+
tokens.add(new TokenOperator(currentToken.toString()));
46+
currentToken.setLength(0);
47+
state = State.INITIAL;
48+
continue;
49+
50+
case INVALID:
51+
throw new InvalidExpressionException(String.format(MESSAGE_ERROR, currentToken));
52+
}
53+
}
54+
55+
finalizeToken(state, currentToken, tokens);
56+
57+
return tokens;
58+
}
59+
60+
private static Character getValidNextCharacter(Expression expression) {
61+
final Optional<Character> currentChar = expression.next();
62+
63+
if (!currentChar.isPresent() || !Grammar.isValidSymbol(currentChar.get())) {
64+
throw new InvalidExpressionException(String.format(MESSAGE_ERROR, currentChar));
65+
}
66+
67+
return currentChar.get();
68+
}
69+
70+
private static void finalizeToken(State state, StringBuilder currentToken, ArrayList<Token> tokens) {
71+
if (State.INVALID == state || State.OPERATOR == state) {
72+
throw new InvalidExpressionException(String.format(MESSAGE_ERROR, currentToken));
73+
} else if (State.NUMBER == state) {
74+
tokens.add(new TokenNumber(currentToken.toString()));
75+
}
76+
}
77+
78+
private enum State {
79+
INITIAL,
80+
NUMBER,
81+
OPERATOR,
82+
INVALID
83+
}
84+
85+
private enum Grammar {
86+
ADDITION('+'),
87+
SUBTRACTION('-'),
88+
MULTIPLICATION('*'),
89+
DIVISION('/');
90+
91+
private final char symbol;
92+
93+
Grammar(char symbol) {
94+
this.symbol = symbol;
95+
}
96+
97+
public static boolean isOperator(char character) {
98+
return Arrays.stream(Grammar.values())
99+
.anyMatch(grammar -> grammar.symbol == character);
100+
}
101+
102+
public static boolean isDigit(char character) {
103+
return Character.isDigit(character);
104+
}
105+
106+
public static boolean isWhitespace(char character) {
107+
return Character.isWhitespace(character);
108+
}
109+
110+
public static boolean isValidSymbol(char character) {
111+
return isOperator(character) || isWhitespace(character) || isDigit(character);
112+
}
113+
114+
}
115+
116+
}
117+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.baeldung.lexer;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Optional;
6+
import java.util.regex.Matcher;
7+
import java.util.regex.Pattern;
8+
9+
class LexerRegex implements Lexer {
10+
11+
private static final String NUMBER_REGEX = "\\d+";
12+
private static final String OPERATOR_REGEX = "[+\\-*/]";
13+
private static final String WHITESPACE_REGEX = "\\s+";
14+
private static final String VALID_EXPRESSION_REGEX = "^(" + NUMBER_REGEX + "(" + OPERATOR_REGEX + NUMBER_REGEX + ")*|" + NUMBER_REGEX + " )$";
15+
16+
private static final Pattern TOKEN_PATTERN = Pattern.compile(NUMBER_REGEX + "|" + OPERATOR_REGEX + "|" + WHITESPACE_REGEX);
17+
18+
private LexerRegex() {
19+
}
20+
21+
public List<Token> tokenize(Expression expression) {
22+
List<Token> tokens = new ArrayList<>();
23+
Matcher matcher = TOKEN_PATTERN.matcher(expression.getValue());
24+
25+
if (!expression.getValue()
26+
.matches(VALID_EXPRESSION_REGEX)) {
27+
throw new InvalidExpressionException(String.format(MESSAGE_ERROR, expression));
28+
}
29+
30+
while (matcher.find()) {
31+
String match = matcher.group();
32+
createToken(match).ifPresent(tokens::add);
33+
}
34+
35+
return tokens;
36+
}
37+
38+
private static Optional<Token> createToken(String match) {
39+
if (match.matches(NUMBER_REGEX)) {
40+
return Optional.of(new TokenNumber(match));
41+
} else if (match.matches(OPERATOR_REGEX)) {
42+
return Optional.of(new TokenOperator(match));
43+
} else if (match.matches(WHITESPACE_REGEX)) {
44+
return Optional.empty();
45+
} else {
46+
throw new InvalidExpressionException(String.format(MESSAGE_ERROR, match));
47+
}
48+
}
49+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.baeldung.lexer;
2+
3+
public abstract class Token {
4+
5+
private final String value;
6+
private final TokenType type;
7+
8+
protected Token(TokenType type, String value) {
9+
this.type = type;
10+
this.value = value;
11+
}
12+
13+
public TokenType getType() {
14+
return type;
15+
}
16+
17+
public String getValue() {
18+
return value;
19+
}
20+
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.lexer;
2+
3+
public class TokenNumber extends Token {
4+
5+
protected TokenNumber(String value) {
6+
super(TokenType.NUMBER, value);
7+
}
8+
9+
public int getValueAsInt() {
10+
return Integer.parseInt(getValue());
11+
}
12+
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.baeldung.lexer;
2+
3+
public class TokenOperator extends Token {
4+
5+
protected TokenOperator(String value) {
6+
super(TokenType.OPERATOR, value);
7+
}
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.baeldung.lexer;
2+
3+
public enum TokenType {
4+
NUMBER,
5+
OPERATOR,
6+
}

0 commit comments

Comments
 (0)