Base implementation has been done
This commit is contained in:
parent
a5265c353e
commit
5f54615465
14
build.gradle
14
build.gradle
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "net.java.openjdk.shinyafox.jshell.gradle.plugin" version "1.0.4"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -21,3 +22,16 @@ jar {
|
|||
attributes 'Main-Class': 'serene.simple.Main'
|
||||
}
|
||||
}
|
||||
|
||||
// run {
|
||||
// standardInput = System.in
|
||||
// }
|
||||
|
||||
task repl(type: JavaExec) {
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
|
||||
main = 'serene.simple.Main'
|
||||
standardInput = System.in
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ public class DefSpecialForm extends SpecialForm {
|
|||
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
SymbolNode sym = (SymbolNode) this.node.rest.first;
|
||||
scope.putValue(sym.name,
|
||||
this.node.rest.rest.first.eval(scope));
|
||||
SymbolNode sym = (SymbolNode) this.node.rest().first();
|
||||
scope.insertSymbol(sym.name,
|
||||
this.node.rest().rest().first().eval(scope));
|
||||
return ListNode.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package serene.simple;
|
||||
|
||||
public class FalseNode extends Node {
|
||||
public boolean isTruthy = false;
|
||||
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -9,22 +9,28 @@ public class FnSpecialForm extends SpecialForm {
|
|||
|
||||
@Override
|
||||
public Object eval(final Scope parentScope) {
|
||||
final ListNode formalParams = (ListNode) this.node.rest.far;
|
||||
final ListNode body = this.node.rest.rest;
|
||||
return new Function() {
|
||||
final ListNode<Node> formalParams = (ListNode<Node>) this.node.rest().first();
|
||||
final ListNode<Node> body = this.node.rest().rest();
|
||||
|
||||
return new Function<Object, Object>() {
|
||||
@Override
|
||||
public Object apply(Object arg) {
|
||||
Object args[] = {arg};
|
||||
return this.apply(args);
|
||||
}
|
||||
|
||||
public Object apply(Object... args) {
|
||||
Scope scope = new Scope(parentScope);
|
||||
if (args.length != formalParams.length()) {
|
||||
if (args.length != formalParams.length) {
|
||||
throw new RuntimeException(String.format("Wrong number of arguments. Expected: %s, Got: %s.",
|
||||
formalParams.length(),
|
||||
formalParams.length,
|
||||
args.length));
|
||||
}
|
||||
// Map parameter values to formal parameter names
|
||||
int i = 0;
|
||||
for (Node param : formalParams) {
|
||||
SymbolNode paramSymbol = (SymbolNode) param;
|
||||
scope.putValue(paramSymbol.name, args[i]);
|
||||
scope.insertSymbol(paramSymbol.name, args[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package serene.simple;
|
||||
|
||||
|
||||
public class IfSpecialForm extends SpecialForm {
|
||||
|
||||
private Node pred;
|
||||
private Node ifNode;
|
||||
private Node elseNode;
|
||||
|
||||
public IfSpecialForm(ListNode<Node> l) {
|
||||
super(l);
|
||||
this.pred = l.rest().first();
|
||||
this.ifNode = l.rest().rest().first();
|
||||
this.elseNode = l.rest().rest().rest().first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(final Scope scope) {
|
||||
Object result = this.pred.eval(scope);
|
||||
if (result == null) {
|
||||
return this.elseNode.eval(scope);
|
||||
}
|
||||
return this.ifNode.eval(scope);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,97 @@
|
|||
package serene.simple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class ListNode<T extends Node> extends Node implements Iterable<T> {
|
||||
public static final ListNode<?> EMPTY = new ListNode<>();
|
||||
|
||||
public final T first;
|
||||
public final ListNode<T> rest;
|
||||
public final int length;
|
||||
|
||||
public ListNode() {
|
||||
this.first = null;
|
||||
this.rest = null;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
public ListNode(T f, ListNode<T> r) {
|
||||
this.first = f;
|
||||
this.rest = r;
|
||||
this.length = r.length + 1;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T extends Node> ListNode<T> list(T... objs) {
|
||||
return list(asList(objs));
|
||||
}
|
||||
|
||||
public static <T extends Node> ListNode<T> list(List<T> objs) {
|
||||
ListNode<T> l = (ListNode<T>) EMPTY;
|
||||
for (int i = objs.size() - 1; i >= 0; i--) {
|
||||
l = l.cons(objs.get(i));
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public ListNode<T> cons(T node) {
|
||||
return new ListNode<T>(node, this);
|
||||
}
|
||||
|
||||
public class ListNode extends Node implements Iterable<Node> {
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
Function f = (Function) this.first.eval(scope);
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
|
||||
for (Node node : this.first) {
|
||||
for (T node : this.rest) {
|
||||
args.add(node.eval(scope));
|
||||
}
|
||||
return f.apply(args.toArray());
|
||||
}
|
||||
|
||||
public T first() {
|
||||
if (this != EMPTY) {
|
||||
return this.first;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ListNode<T> rest() {
|
||||
if (this != EMPTY) {
|
||||
return this.rest;
|
||||
}
|
||||
return (ListNode<T>) EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
private ListNode<T> l = ListNode.this;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.l != EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (this.l == EMPTY) {
|
||||
return null;
|
||||
}
|
||||
T first = this.l.first;
|
||||
this.l = this.l.rest;
|
||||
return first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new SereneException("Iterator is immutable");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package serene.simple;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Scanner;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Console;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -10,19 +13,25 @@ public class Main {
|
|||
public static void main(String[] args) throws IOException {
|
||||
if (args.length == 0) {
|
||||
startRepl();
|
||||
return;
|
||||
}
|
||||
runSerene(args[0]);
|
||||
}
|
||||
|
||||
private static void startRepl() {
|
||||
private static void startRepl() throws IOException {
|
||||
Scope rootScope = Scope.getRootScope();
|
||||
|
||||
Console console = System.console();
|
||||
//Console console = System.console();
|
||||
//BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
Scanner in = new Scanner(System.in);
|
||||
|
||||
System.out.println("Serene 'simple' v0.1.0");
|
||||
while(true) {
|
||||
String inputData = console.readLine("serene-> ");
|
||||
System.out.print("serene-> ");
|
||||
//String inputData = reader.readLine();
|
||||
String inputData = in.nextLine();
|
||||
|
||||
if (expr == null) break;
|
||||
if (inputData == null) break;
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes());
|
||||
ListNode<Node> nodes = Reader.read(inputStream);
|
||||
|
@ -37,6 +46,7 @@ public class Main {
|
|||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package serene.simple;
|
||||
|
||||
public class NilNode extends Node {
|
||||
public boolean isTruthy = false;
|
||||
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package serene.simple;
|
||||
|
||||
public abstract class Node {
|
||||
public boolean isTruthy = true;
|
||||
|
||||
public abstract Object eval(Scope scope);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package serene.simple;
|
||||
|
||||
|
||||
public class QuoteSpecialForm extends SpecialForm {
|
||||
public QuoteSpecialForm(ListNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(final Scope scope) {
|
||||
return this.node;
|
||||
}
|
||||
}
|
|
@ -2,8 +2,10 @@ 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;
|
||||
|
||||
|
||||
|
@ -19,29 +21,31 @@ public class Reader {
|
|||
return readNumber(inputStream);
|
||||
}
|
||||
else if (c == ')') {
|
||||
throw new IllegalArgumentException("Unmatch paranthesis.")
|
||||
throw new IllegalArgumentException("Unmatch paranthesis.");
|
||||
}
|
||||
else {
|
||||
return readSymbol(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static ListNode read(InputStream inputStream) throws IOException {
|
||||
public static ListNode<Node> read(InputStream inputStream) throws IOException {
|
||||
return read(new PushbackReader(new InputStreamReader(inputStream)));
|
||||
}
|
||||
|
||||
public static ListNode read(PushbackReader inputStream) throws IOException {
|
||||
public static ListNode<Node> read(PushbackReader inputStream) throws IOException {
|
||||
List<Node> nodes = new ArrayList<Node>();
|
||||
|
||||
skipWhitespaces(inputStream);
|
||||
skipWhiteSpaces(inputStream);
|
||||
|
||||
char c = inputStream.read();
|
||||
char c = (char) inputStream.read();
|
||||
while ((byte) c != -1) {
|
||||
inputStream.unread(c);
|
||||
nodes.add(readNode(inputStream));
|
||||
skipWhitespaces(inputStream);
|
||||
skipWhiteSpaces(inputStream);
|
||||
c = (char) inputStream.read();
|
||||
}
|
||||
|
||||
return ListNode.list(nodes);
|
||||
}
|
||||
|
||||
private static Node readList(PushbackReader inputStream) throws IOException {
|
||||
|
@ -51,7 +55,7 @@ public class Reader {
|
|||
List<Node> nodes = new ArrayList<Node>();
|
||||
|
||||
do {
|
||||
skipWhitespaces(inputStream);
|
||||
skipWhiteSpaces(inputStream);
|
||||
char c = (char) inputStream.read();
|
||||
|
||||
if (c == ')') {
|
||||
|
@ -63,6 +67,51 @@ public class Reader {
|
|||
nodes.add(readNode(inputStream));
|
||||
}
|
||||
} while(true);
|
||||
return SpecialForm(ListNode.list(nodes));
|
||||
|
||||
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)) {
|
||||
inputStream.unread(ch);
|
||||
break;
|
||||
};
|
||||
str = str + (char) ch;
|
||||
}
|
||||
|
||||
return new SymbolNode(str);
|
||||
}
|
||||
|
||||
private static Node readNumber(PushbackReader inputStream) throws IOException {
|
||||
|
||||
String number = "";
|
||||
|
||||
while (true) {
|
||||
int ch = inputStream.read();
|
||||
if(isWhiteSpace(ch)) {
|
||||
inputStream.unread(ch);
|
||||
break;
|
||||
};
|
||||
number = number + (char) ch;
|
||||
}
|
||||
|
||||
return new NumberNode(Long.parseLong(number, 10));
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.util.HashMap;
|
|||
public class Scope {
|
||||
private final HashMap<String, Object> symbolsMapping = new HashMap<String, Object>();
|
||||
private final Scope parent;
|
||||
private static final Scope root = new Scope(null);
|
||||
|
||||
public Scope() {
|
||||
this(null);
|
||||
|
@ -30,4 +31,8 @@ public class Scope {
|
|||
public void insertSymbol(String symbolName, Object symbolValue) {
|
||||
this.symbolsMapping.put(symbolName, symbolValue);
|
||||
}
|
||||
|
||||
public static Scope getRootScope() {
|
||||
return Scope.root;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package serene.simple;
|
||||
|
||||
|
||||
class SereneException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SereneException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -7,13 +7,13 @@ public class SpecialForm extends Node {
|
|||
private static final SymbolNode IF = new SymbolNode("if");
|
||||
private static final SymbolNode QUOTE = new SymbolNode("quote");
|
||||
|
||||
private final ListNode node;
|
||||
public final ListNode<Node> node;
|
||||
|
||||
public SpecialForm(ListNode node) {
|
||||
public SpecialForm(ListNode<Node> node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public static Node check(ListNode l) {
|
||||
public static Node check(ListNode<Node> l) {
|
||||
if (l == ListNode.EMPTY) {
|
||||
return l;
|
||||
} else if (l.first.equals(DEF)) {
|
||||
|
@ -27,4 +27,9 @@ public class SpecialForm extends Node {
|
|||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
throw new SereneException("Can't use SpecialForm directly");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package serene.simple;
|
||||
|
||||
public class SymbolNode extends Node {
|
||||
private final String name;
|
||||
public final String name;
|
||||
|
||||
public SymbolNode(String name) {
|
||||
this.name = name;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package serene.simple;
|
||||
|
||||
public class TrueNode extends Node {
|
||||
@Override
|
||||
public Object eval(Scope scope) {
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue