203 lines
5.4 KiB
Java
203 lines
5.4 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.EOFException;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.PushbackReader;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
|
|
public class Reader {
|
|
public static Node readNode(PushbackReader inputStream) throws IOException, IllegalArgumentException, SereneException {
|
|
char c = (char) inputStream.read();
|
|
char nextChar = (char) inputStream.read();
|
|
|
|
inputStream.unread(nextChar);
|
|
inputStream.unread(c);
|
|
|
|
if (c == '(') {
|
|
return readList(inputStream);
|
|
}
|
|
else if (c == '"') {
|
|
return readString(inputStream);
|
|
}
|
|
else if (Character.isDigit(c) ||
|
|
(c == '-' && Character.isDigit(nextChar))) {
|
|
return readNumber(inputStream);
|
|
}
|
|
else if (c == ')') {
|
|
throw new IllegalArgumentException("Unmatch paranthesis.");
|
|
}
|
|
else {
|
|
return readSymbol(inputStream);
|
|
}
|
|
}
|
|
|
|
public static ListNode<Node> read(InputStream inputStream) throws IOException, IllegalArgumentException, SereneException {
|
|
// The number two is the buffer size, we need a bigger buffer than
|
|
// just one byte to be able to read ahead and undo reads
|
|
return read(new PushbackReader(new InputStreamReader(inputStream), 2));
|
|
}
|
|
|
|
public static ListNode<Node> read(PushbackReader inputStream) throws IOException, IllegalArgumentException, SereneException {
|
|
List<Node> nodes = new ArrayList<Node>();
|
|
skipWhiteSpaces(inputStream);
|
|
|
|
char c = (char) inputStream.read();
|
|
while ((byte) c != -1) {
|
|
if (c == ';') {
|
|
break;
|
|
}
|
|
|
|
inputStream.unread(c);
|
|
nodes.add(readNode(inputStream));
|
|
skipWhiteSpaces(inputStream);
|
|
c = (char) inputStream.read();
|
|
}
|
|
|
|
return ListNode.list(nodes);
|
|
}
|
|
|
|
private static Node readList(PushbackReader inputStream) throws IOException, SereneException {
|
|
char opening = (char) inputStream.read();
|
|
assert opening == '(' : "Lists must start with a '('";
|
|
|
|
List<Node> nodes = new ArrayList<Node>();
|
|
|
|
do {
|
|
skipWhiteSpaces(inputStream);
|
|
char c = (char) inputStream.read();
|
|
|
|
if (c == ')') {
|
|
break;
|
|
} else if ((byte) c == -1) {
|
|
throw new EOFException("EOF reached before closing of list");
|
|
} else {
|
|
inputStream.unread(c);
|
|
nodes.add(readNode(inputStream));
|
|
}
|
|
} while(true);
|
|
|
|
return SpecialForm.check(ListNode.list(nodes));
|
|
}
|
|
|
|
private static Node readSymbol(PushbackReader inputStream) throws IOException {
|
|
String str = "";
|
|
|
|
while (true) {
|
|
int ch = inputStream.read();
|
|
|
|
if(isWhiteSpace(ch) || isClosingChar(ch) || ch == -1) {
|
|
inputStream.unread(ch);
|
|
break;
|
|
};
|
|
str = str + (char) ch;
|
|
}
|
|
|
|
switch(str) {
|
|
case "true":
|
|
return new TrueNode();
|
|
case "false":
|
|
return new FalseNode();
|
|
case "nil":
|
|
return new NilNode();
|
|
default:
|
|
return new SymbolNode(str);
|
|
}
|
|
|
|
}
|
|
|
|
private static Node readNumber(PushbackReader inputStream) throws IOException {
|
|
int ch = inputStream.read();
|
|
String number = "";
|
|
|
|
if (Character.isDigit((char) ch) || ch == '-') {
|
|
number = number + (char) ch;
|
|
}
|
|
|
|
ch = inputStream.read();
|
|
|
|
while (Character.isDigit((char) ch) || (char) ch == '.') {
|
|
number = number + (char) ch;
|
|
ch = inputStream.read();
|
|
}
|
|
|
|
inputStream.unread(ch);
|
|
return new NumberNode(number);
|
|
}
|
|
|
|
private static Node readString(PushbackReader inputStream) throws IOException {
|
|
char opening = (char) inputStream.read();
|
|
assert opening == '"' : "Strings should start with '\"'";
|
|
String str = "";
|
|
|
|
while (true) {
|
|
char ch = (char) inputStream.read();
|
|
|
|
if(ch == '"') {
|
|
break;
|
|
}
|
|
str = str + ch;
|
|
};
|
|
|
|
return new StringNode(str);
|
|
}
|
|
|
|
private static char readAndIgnoreComments(PushbackReader inputStream) throws IOException {
|
|
char firstChar = (char) inputStream.read();
|
|
|
|
if (firstChar == ';') {
|
|
while(true) {
|
|
char ch = (char) inputStream.read();
|
|
|
|
if(ch == -1 || ch == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
return readAndIgnoreComments(inputStream);
|
|
}
|
|
else {
|
|
return firstChar;
|
|
}
|
|
}
|
|
|
|
private static void skipWhiteSpaces(PushbackReader inputStream) throws IOException {
|
|
int ch = inputStream.read();
|
|
|
|
while (isWhiteSpace(ch)) {
|
|
ch = inputStream.read();
|
|
}
|
|
inputStream.unread(ch);
|
|
}
|
|
|
|
private static boolean isWhiteSpace(int ch) {
|
|
return (ch == ' ' || ch == '\t' || ch == '\f' || ch == '\r' || ch == '\n');
|
|
}
|
|
|
|
private static boolean isClosingChar(int ch) {
|
|
return (ch == ')' || ch == ']' ||
|
|
ch == '}');
|
|
}
|
|
}
|