Skip to content

Commit 77da496

Browse files
authored
Create README.en.md
1 parent ca0b065 commit 77da496

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed

README.en.md

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
# Interpreter Design Pattern
2+
[![pl](https://img.shields.io/badge/lang-pl-ff0000)](https://github.com/koder95/Interpreter/blob/master/README.md)
3+
[![en](https://img.shields.io/badge/lang-en-blue)](https://github.com/koder95/Interpreter/blob/master/README.en.md)
4+
5+
[![Java CI with Maven](https://github.com/koder95/Interpreter/actions/workflows/maven.yml/badge.svg)](https://github.com/koder95/Interpreter/actions/workflows/maven.yml)
6+
7+
This project is an implementation of the *Interpreter* design pattern in a universal version.
8+
9+
The [client](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Client.java) interface allows creating a new instance of the [interpreter](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Interpreter.java), which will operate in the given [context](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Context.java).
10+
11+
Formal languages can be divided into two groups based on context: context-free formal languages and context-sensitive formal languages. The context determines the final meaning of ambiguous [expressions](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Expression.java) from the perspective of only one expression. If each of the expressions is unambiguous and does not need clarification by the context, then an instance created by `new pl.koder95.interpreter.Context(){}` should be used, which means context-free grammar.
12+
13+
The [Interpreter](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Interpreter.java) interface not only provides a [method for interpreting read values](https://github.com/koder95/Interpreter/blob/b3f9b37f580d7580785ac4515601e07ce9b38d85/src/main/java/pl/koder95/interpreter/Interpreter.java#L38), but also implementations of the [parser](https://github.com/koder95/Interpreter/blob/master/src/main/java/pl/koder95/interpreter/Parser.java) interface, which is used to build an abstract syntax tree using the method [buildAbstractSyntaxTree(java.util.Queue)](https://github.com/koder95/Interpreter/blob/2d2197598c8597dac693b7520f52d9bac62c9ade/src/main/java/pl/koder95/interpreter/Parser.java#L23).
14+
15+
## Maven Dependency
16+
```xml
17+
<dependency>
18+
<groupId>pl.koder95</groupId>
19+
<artifactId>interpreter</artifactId>
20+
<version>1.0.0</version>
21+
</dependency>
22+
```
23+
24+
## Example
25+
```java
26+
package pl.koder95.interpreter;
27+
28+
import java.io.StringReader;
29+
import java.util.*;
30+
31+
/**
32+
* Class containing an example implementation of the interpreter.
33+
* The implemented language is context-sensitive and defines sorting of strings using the instructions
34+
* {@code ORDER BY ASC}, {@code ORDER BY DSC}, or {@code ORDER BY REV}.
35+
* For some order: the language is called {@code SortExaL} from
36+
* the English <i><b>Sort</b>ing <b>Exa</b>mple <b>L</b>anguage</i>.
37+
*/
38+
public final class Example {
39+
40+
private Example() {}
41+
42+
/**
43+
* Method interpreting the source characters and input lines.
44+
* @param readable the source of characters to be interpreted
45+
* @param lines array of lines placed in the context before the interpretation process
46+
* @return list of lines sorted according to the instructions
47+
*/
48+
public static List<String> interpret(Readable readable, String... lines) {
49+
// The following example shows how to use the interpreter.
50+
// The first step is to create a client object:
51+
ExampleClient client = new ExampleClient();
52+
// First, create an instance of the context that will be used by the interpreter:
53+
ExampleContext context = new ExampleContext();
54+
if (lines.length > 0) { // Check if there are any lines in the input array
55+
context.lines.addAll(List.of(lines)); // Add lines to the context
56+
}
57+
// To create a new instance of the interpreter, do it through the client:
58+
ExampleInterpreter interpreter = client.newInterpreter(context);
59+
// The interpreter interprets the source characters and returns the result as a list of lines:
60+
return interpreter.interpret(readable);
61+
}
62+
63+
public static void main(String[] args) {
64+
// Example instructions:
65+
String expressions = """
66+
B
67+
D
68+
C
69+
ORDER BY DSC
70+
A
71+
ORDER BY REV
72+
E
73+
ORDER BY REV
74+
ORDER BY REV
75+
F
76+
""";
77+
StringReader readable = new StringReader(expressions);
78+
// The interpreter interprets and returns the result as a list of lines:
79+
List<String> lines = args.length > 0 ? interpret(readable, String.join(" ", args)) : interpret(readable);
80+
// Print the interpretation results:
81+
System.out.println(lines); // Expected output (if no arguments): [A, B, C, D, E, F]
82+
}
83+
84+
/*
85+
IMPLEMENTATIONS
86+
================
87+
* ExampleContext – implements a sample context for a context-sensitive language
88+
* Line – implements a non-terminal expression representing a line of text
89+
* OrderByType – defines types of ways to order lines of text
90+
* Sort – implements a terminal expression that returns the sorted lines according to the specified way
91+
* Add – implements a terminal expression that adds a line of text to the context
92+
* Instructions – implements a terminal expression containing a list of other terminal expressions
93+
* LineTokenizer – tokenizes by dividing the source characters into lines
94+
* ExampleParser – implements a parser that creates AST (returns Instructions)
95+
* ExampleInterpreter – implements an interpreter by defining a static parser and a variable context
96+
* ExampleClient – implements a client creating the interpreter
97+
98+
LineTokenizer takes characters and transforms them into lines (Line).
99+
The parser (ExampleParser) analyzes the lines and defines their meaning.
100+
Lines are transformed by the parser into Add or Sort, which are added to a list of instructions (Instructions).
101+
The interpreter (ExampleInterpreter) interprets the instructions and returns a list of sorted lines.
102+
The client deals with creating the interpreter, depending on the context (ignored here).
103+
*/
104+
private static class ExampleContext implements Context {
105+
private final List<String> lines = new LinkedList<>();
106+
}
107+
108+
private record Line(String content) implements NonTerminalExpression<String> {
109+
@Override
110+
public String getObject() {
111+
return content;
112+
}
113+
}
114+
115+
private enum OrderByType {
116+
ASC, DSC, REV
117+
}
118+
119+
private record Sort(OrderByType orderBy) implements TerminalExpression<ExampleContext, List<String>> {
120+
121+
@Override
122+
public List<String> interpret(ExampleContext context) {
123+
if (orderBy == OrderByType.ASC) context.lines.sort(Comparator.naturalOrder());
124+
else if (orderBy == OrderByType.DSC) context.lines.sort(Comparator.reverseOrder());
125+
else if (orderBy == OrderByType.REV) Collections.reverse(context.lines);
126+
List<String> ordered = new ArrayList<>(context.lines);
127+
context.lines.clear();
128+
return ordered;
129+
}
130+
131+
@Override
132+
public String asString() {
133+
return "ORDER BY " + orderBy.name();
134+
}
135+
}
136+
137+
private record Add(Line line) implements TerminalExpression<ExampleContext, List<String>> {
138+
@Override
139+
public List<String> interpret(ExampleContext context) {
140+
if (line != null) context.lines.add(line.content);
141+
return context.lines;
142+
}
143+
144+
@Override
145+
public String asString() {
146+
return line == null ? "null" : line.content();
147+
}
148+
}
149+
150+
private record Instructions(List<TerminalExpression<ExampleContext, List<String>>> terminalExpressionList)
151+
implements TerminalExpression<ExampleContext, List<String>> {
152+
@Override
153+
public List<String> interpret(ExampleContext context) {
154+
for (TerminalExpression<ExampleContext, List<String>> expr : terminalExpressionList) {
155+
List<String> interpreted = expr.interpret(context);
156+
if (expr instanceof Sort) {
157+
context.lines.addAll(interpreted);
158+
}
159+
}
160+
return context.lines;
161+
}
162+
163+
@Override
164+
public String asString() {
165+
return "INSTRUCTIONS";
166+
}
167+
}
168+
169+
private static final class LinesTokenizer extends Tokenizer {
170+
private final Queue<Line> lineQueue = new LinkedList<>();
171+
private Scanner scanner = null;
172+
173+
@Override
174+
public Line next() {
175+
while (scanner != null && scanner.hasNextLine()) {
176+
String content = scanner.nextLine();
177+
lineQueue.add(new Line(content));
178+
}
179+
return lineQueue.poll();
180+
}
181+
182+
@Override
183+
public boolean hasNext() {
184+
return (scanner != null && scanner.hasNext()) || !lineQueue.isEmpty();
185+
}
186+
187+
@Override
188+
public void setSource(Readable source) {
189+
super.setSource(source);
190+
scanner = new Scanner(source);
191+
}
192+
}
193+
194+
private static final class ExampleParser implements Parser<ExampleContext, List<String>> {
195+
public static final Tokenizer TOKENIZER = new LinesTokenizer();
196+
197+
@Override
198+
public Tokenizer getTokenizer() {
199+
return TOKENIZER;
200+
}
201+
202+
@Override
203+
public TerminalExpression<ExampleContext, List<String>> buildAbstractSyntaxTree(Queue<NonTerminalExpression<?>> tokens) {
204+
List<TerminalExpression<ExampleContext, List<String>>> terminalExpressionList = new LinkedList<>();
205+
while (!tokens.isEmpty()) {
206+
NonTerminalExpression<?> token = tokens.poll();
207+
if (token instanceof Line line) {
208+
String content = line.content();
209+
String tag = "ORDER BY";
210+
if (content.startsWith(tag)) {
211+
Sort sorting = recognizeSort(content.substring(tag.length()).strip());
212+
terminalExpressionList.add(sorting);
213+
} else {
214+
terminalExpressionList.add(new Add(line));
215+
}
216+
} else {
217+
terminalExpressionList.add(new Add(new Line(token.asString())));
218+
}
219+
}
220+
return new Instructions(terminalExpressionList);
221+
}
222+
223+
private static Sort recognizeSort(String type) {
224+
Sort sorting;
225+
if (type.equals(OrderByType.ASC.name())) {
226+
sorting = new Sort(OrderByType.ASC);
227+
} else if (type.equals(OrderByType.DSC.name())) {
228+
sorting = new Sort(OrderByType.DSC);
229+
} else if (type.equals(OrderByType.REV.name())) {
230+
sorting = new Sort(OrderByType.REV);
231+
} else if (type.isBlank()) {
232+
sorting = new Sort(null);
233+
} else {
234+
throw new SyntaxException("Unknown ordering type");
235+
}
236+
return sorting;
237+
}
238+
}
239+
240+
private record ExampleInterpreter(ExampleContext context) implements Interpreter<ExampleContext, List<String>> {
241+
public static final ExampleParser PARSER = new ExampleParser();
242+
243+
@Override
244+
public ExampleContext getContext() {
245+
return context;
246+
}
247+
248+
@Override
249+
public ExampleParser getParser() {
250+
return PARSER;
251+
}
252+
}
253+
254+
private static final class ExampleClient implements Client<ExampleContext, List<String>, ExampleInterpreter> {
255+
@Override
256+
public ExampleInterpreter newInterpreter(ExampleContext context) {
257+
return new ExampleInterpreter(context);
258+
}
259+
}
260+
}
261+
```

0 commit comments

Comments
 (0)