198 lines
6.2 KiB
Java
198 lines
6.2 KiB
Java
/**
|
|
* 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;
|
|
import java.io.BufferedReader;
|
|
import java.io.InputStreamReader;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
|
|
/**
|
|
* 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.
|
|
* * Decent [functional] data structures. The only data structure I implemented
|
|
* is list.
|
|
* * Quality code. The general quality of this implementation is not great, I
|
|
* sacrificed quality for time.
|
|
*/
|
|
public class Main {
|
|
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;
|
|
}
|
|
|
|
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());
|
|
|
|
|
|
try {
|
|
ListNode<Node> nodes = Reader.read(inputStream);
|
|
Object result = ListNode.EMPTY;
|
|
|
|
for (Node n : nodes) {
|
|
result = n.eval(rootScope);
|
|
}
|
|
|
|
System.out.print(";; ");
|
|
if (result == null) {
|
|
System.out.println("nil");
|
|
}
|
|
else {
|
|
System.out.println(result.toString());
|
|
}
|
|
|
|
}
|
|
catch(Exception e) {
|
|
System.out.println("Error: ");
|
|
e.printStackTrace(System.out);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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));
|
|
|
|
// NOTE: The nRepl server is too simple. It supports one connection at a time
|
|
// and will terminate when the client disconnects.
|
|
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);
|
|
}
|
|
}
|
|
}
|