/** * Serene (simple) - A PoC lisp to collect data on Serenes concepts * Copyright (C) 2019-2020 Sameer Rahmani * * 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 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 read(PushbackReader inputStream) throws IOException, IllegalArgumentException, SereneException { List nodes = new ArrayList(); 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 nodes = new ArrayList(); 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)) { number = number + (char) ch; ch = inputStream.read(); } inputStream.unread(ch); return new NumberNode(Long.parseLong(number, 10)); } 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 == '}'); } }