For the time being, I will try to create REPL in Java 8.
REPL (Read (read input) --Eval (execution) --Print (result printing) -Loop (loop of 3 functions on the left)) is an environment to execute a program interactively, mainly Python. It is a function that is familiar to the script program and Common Lisp. In addition, console tools (such as SQLPlus) that come with the DBMS have similar functionality in terms of interpreting and executing input and outputting the results.
We'll create the foundation for a Java console program, rather than the interactive shell of the programming language, rather than the latter, the REPL in the broader sense.
I referred to the following article. (Sorry for the delay in description) Create a REPL (Read-eval-print loop).
First, the requirements for the REPL to be created.
--Implement only Java 8 functions. (Do not use the SDK and libraries that need to be downloaded separately) --The main focus is not on the execution environment of the program language, but on the interface that calls Java programs internally. --The first word of the input string becomes the command name.
Please note that it is an oleore style that deviates from the general description of Java. There are the following reasons. It is a reprint + α of the previous entry, Making lexical analysis with Java 8 (1).
--To keep the upper program simple, do not throw exceptions or return nulls as much as possible. I want to avoid burying the processing body in a large number of error checks.
--Minimize scope.
--Use immutable objects wherever possible.
--Do not use general getters and setters. (Getter / Setter is evil. That's it. Original [Getters / Setters. Evil. Period.](Https: See //www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html)
--No annotations to make the source compact.
--In principle, the import statement is not used in order to get used to the Java standard library and to clarify the location of the function. Importing Stream-related classes, which tend to be redundant.
--In order to reduce the number of source files, small classes and interfaces may be combined into one file.
―― ~~ Because I am tired of the long and deep nesting sentences at work, I often use ~~ early returns.
Surprisingly, there are many negatives, but I use it to check errors during processing, prevent the flow of the processing body from being buried by deep nesting, or because I want you to read the return at the beginning of the method as an expression. I am.
When writing the article, I was wondering whether to explain it from the top down or the bottom up, but to clarify the whole picture, I will explain from the top. If you come across a class name that you don't know, please look at it with the feeling of "I'm going to explain the details from now on?"
First, the big picture of the abstract class BaseREPL.
BaseREPL.java
abstract class BaseREPL {
enum States {
Quit, //End
Finished, // Cleanup()Done
Error, //error
NotFind, //No target command
Input, //input(default)
Incomplete, //Input not completed(Used to support multi-line input)
Complete, //input completed
}
BaseREPL(String encoding) {
if ( encoding == null || encoding.trim().isEmpty() ) { encoding = "UTF-8"; }
console_ = Service.Console.create(encoding);
}
/**Main loop.(template method) */
public final void run() {
if ( state_ == States.Finished ) { return; }
try {
welcome();
while (state_ != States.Quit) {
state_ = response( eval( accept(prompt_) ) );
}
goodbye();
} finally {
cleanup();
state_ = States.Finished;
}
}
// user defined methods ==========================================================================
abstract protected String accept(String prompt); //Prompt display / input reception
abstract protected States eval(String input); //Input character string interpretation / execution
abstract protected States response(States state); // eval()Post-processing
abstract protected void cleanup(); //Clean up at the end of the REPL
abstract protected void welcome(); //Display of REPL start string
abstract protected void goodbye(); //Display of REPL end string
abstract protected void help(); //Display help string
// print strings to console ======================================================================
public final void print(String format, Object... args) { console_.print(format, args); }
public final void print(String s) { console_.print(s); }
public final void print(String[] sz) { for (String s : sz) { print(s); } }
public final void print(java.util.Collection<String> sz) { for (String s : sz) { print(s); } }
// internal fields ===============================================================================
protected final Interface.Console console_ = Service.Console.get();
protected String prompt_ = "REPL> ";
protected States state_ = States.Input;
}
A description of the source. However, because it is an abstract class, it is almost empty.
** Added on October 28, 2018 ** Added a constructor that receives the console encoding name. If null or whitespace is passed, it defaults to "UTF-8".
python
BaseREPL(String encoding) {
if ( encoding == null || encoding.trim().isEmpty() ) { encoding = "UTF-8"; }
console_ = Service.Console.create(encoding);
}
protected final Interface.Console console_;
The main loop is a fixed process.
`welcome ()`
, first of all, a greeting when REPL starts.accept ()
.eval () `` `, interpret and execute the character string entered with
accept () ```.`response ()`
, execute post-processing after receiving the processing result of ```eval ()` ``.`goodbye ()`
to say hello to the end of the REPL.`cleanup ()`
to clean up.enum states
Is an enumeration that represents the state of the repl.
This class uses only States.Finished and States.Quit.
The other enumerators have not been decided how to use them, despite the comments. You can use it as you like at the inheritance destination. (I can't think of any use other than States.input, States.Error, and States.Missing ...)
Since it is assumed that cleanup () will clean up the used objects and resources, once the run () method is exited, there is a high possibility that it will end abnormally due to lack of resources, so at the beginning of the method I have checked whether it has been completed. Also, as I wrote in the notes, I will describe in a style that does not throw exceptions as much as possible, but even if an exception occurs without stopping, cleanup () is surely executed so that the processing body is `try Enclose it in {} ``` and put the cleanup () method call in the`
finally {} ``` clause.
Therefore, do not use exit ().
enum States {
Quit, //End
Finished, // Cleanup()Done
Error, //error
NotFind, //No target command
Input, //input(default)
Incomplete, //Input not completed(Used to support multi-line input)
Complete, //input completed
}
/**Main loop.(template method) */
public final void run() {
if ( state_ == States.Finished ) { return; }
try {
welcome();
while (state_ != States.Quit) {
state_ = response( eval( accept(prompt_) ) );
}
goodbye();
} finally {
cleanup();
state_ = States.Finished;
}
}
protected String prompt_ = "REPL> ";
protected States state_ = States.Input;
The methods contained here are classes that must be implemented by a class that inherits from BaseREPL. I've explained what the method does in the main loop, so I'll omit it.
help()
Is not used in the main loop. This methodeval()
So, when it is interpreted as displaying help, and when supporting help text, implement it in the inherited class.
python
abstract protected String accept(String prompt); //Prompt display / input reception
abstract protected States eval(String input); //Input character string interpretation / execution
abstract protected States response(States state); //Post-eval post-processing
abstract protected void cleanup(); //Clean up at the end of the REPL
abstract protected void welcome(); //Display of REPL start string
abstract protected void goodbye(); //Display of REPL end string
abstract protected void help(); //Display help string
A method that provides output to the console. All method names are unified to print, and 4 types of overloads depending on the argument type are prepared.
prints the received string + newline. Corresponds to println ().``, ``
print (Collection These methods eventually delegate processing to the Interface.Console object. This object will be explained at a later date (probably next time).
public final void print(String format, Object... args) { console_.print(format, args); }
public final void print(String s) { console_.print(s); }
public final void print(String[] sz) { for (String s : sz) { print(s); } }
public final void print(java.util.Collection<String> sz) { for (String s : sz) { print(s); } }
protected final Interface.Console console_;
The above is the explanation of the base class of REPL.
As you can see from the source, the `run ()`
method is GoF [design pattern](https://qiita. com / tags /% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83 It corresponds to Template Method of% B3).
To express it, it is defined as an abstract class, not as an interface.
In addition to this, methods that do not depend on the implementation of the inheritance destination have been implemented with the final qualifier.
Based on this abstract class, I would like to go with a concrete implementation of the REPL class next time, but since I have various desires, I plan to create a mechanism to be used in the inherited class.
Recommended Posts