serene-simple/src/main/java/serene/simple/Main.java

198 lines
6.2 KiB
Java
Raw Normal View History

/**
* Serene (simple) - A PoC lisp to collect data on Serenes concepts
* Copyright (C) 2019-2020 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package serene.simple;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.Console;
import java.io.FileInputStream;
2020-01-24 17:11:56 +00:00
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.ServerSocket;
import java.net.Socket;
2020-01-01 13:17:25 +00:00
/**
* What is missing:
* * A namespace functionality. Because creating and compiling dynamic classes
* is a rabbit hole and tons of work which doesn't make sense for a toy
* project.
* * Unified function interface.
* * Requiring different namespaces
* * A sophisticated parser. My Reader implementation is really cheap that
* suits a toy project. It might worth investigating on different solutions
* including using a parser generator or a head of time read implementation.
* * Primitive functions in Serene. I implemented lots of primitive functions
* in java rather than Serene itself mostly because of two reasons. Lack of
* macros and namespaces.
2020-01-01 16:28:14 +00:00
* * Decent [functional] data structures. The only data structure I implemented
* is list.
2020-01-01 13:17:25 +00:00
* * Quality code. The general quality of this implementation is not great, I
* sacrificed quality for time.
*/
public class Main {
2020-01-24 17:11:56 +00:00
private static int port = 5544;
private static String host = "localhost";
private static String licenseHeader = "\nSerene(simple), Copyright (C) 2019-2020 " +
"Sameer Rahmani <lxsameer@gnu.org>\n" +
"Serene(simple) comes with ABSOLUTELY NO WARRANTY;\n" +
"This is free software, and you are welcome\n" +
"to redistribute it under certain conditions; \n" +
"for details take a look at the LICENSE file.\n";
public static void main(String[] args) throws IOException, RuntimeException {
if (args.length == 0) {
startRepl();
return;
}
2020-01-24 17:11:56 +00:00
if (args[0].equals("nrepl")) {
nrepl();
return;
}
runSerene(args[0]);
}
private static void startRepl() throws IOException {
RootScope rootScope = new RootScope();
Console console = System.console();
System.out.println("Serene 'simple' v0.1.0");
System.out.println(licenseHeader);
while(true) {
String inputData = console.readLine("serene-> ");
if (inputData == null) break;
ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes());
2019-12-10 17:28:58 +00:00
2019-12-18 14:02:04 +00:00
try {
ListNode<Node> nodes = Reader.read(inputStream);
Object result = ListNode.EMPTY;
2019-12-10 17:28:58 +00:00
for (Node n : nodes) {
result = n.eval(rootScope);
}
2020-01-01 16:28:14 +00:00
System.out.print(";; ");
if (result == null) {
System.out.println("nil");
}
2020-01-01 16:28:14 +00:00
else {
System.out.println(result.toString());
}
}
catch(Exception e) {
System.out.println("Error: ");
e.printStackTrace(System.out);
}
}
}
2020-01-26 11:36:38 +00:00
/**
* Serene's nRepl is super simple. It waits for a newline char and then evaluates the
* given input. It sends back the result of the evaluation in `<status-char><value>` format.
* `status-char` is either `0` or `1`. **Zero** means the evaluation was successful and the `value`
* is the result of evaluation while **one** means there was an error during the evaluation and
* the `value` is the traceback for the exception.
*/
2020-01-24 17:11:56 +00:00
public static void nrepl() throws IOException {
RootScope rootScope = new RootScope();
ServerSocket socket = new ServerSocket(Main.port);;
System.out.println("Serene 'simple' v0.1.0");
System.out.println(licenseHeader);
System.out.println(
String.format("nRepl has been started on tcp://%s:%s", Main.host, Main.port));
2020-01-26 11:36:38 +00:00
// NOTE: The nRepl server is too simple. It supports one connection at a time
// and will terminate when the client disconnects.
2020-01-24 17:11:56 +00:00
try {
Socket clientSocket = socket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while(true) {
String inputData = in.readLine();
if (inputData == null) break;
ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes());
try {
ListNode<Node> nodes = Reader.read(inputStream);
Object result = ListNode.EMPTY;
for (Node n : nodes) {
result = n.eval(rootScope);
}
if (result == null) {
out.println("0nil");
}
else {
out.println(
String.format("0%s", result.toString()));
}
}
catch(Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
out.println(
String.format("1%s", exceptionAsString));
}
}
}
catch (IOException e) {
System.out.println("Exception caught when trying to listen on port "
+ Main.port + " or listening for a connection");
System.out.println(e.getMessage());
}
finally {
socket.close();
}
}
private static void runSerene(String filePath) throws IOException {
IScope rootScope = new RootScope();
FileInputStream input = new FileInputStream(filePath);
ListNode<Node> nodes = Reader.read(input);
for (Node n : nodes) {
n.eval(rootScope);
}
}
}