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 {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
|
id "net.java.openjdk.shinyafox.jshell.gradle.plugin" version "1.0.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -21,3 +22,16 @@ jar {
|
||||||
attributes 'Main-Class': 'serene.simple.Main'
|
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
|
@Override
|
||||||
public Object eval(Scope scope) {
|
public Object eval(Scope scope) {
|
||||||
SymbolNode sym = (SymbolNode) this.node.rest.first;
|
SymbolNode sym = (SymbolNode) this.node.rest().first();
|
||||||
scope.putValue(sym.name,
|
scope.insertSymbol(sym.name,
|
||||||
this.node.rest.rest.first.eval(scope));
|
this.node.rest().rest().first().eval(scope));
|
||||||
return ListNode.EMPTY;
|
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
|
@Override
|
||||||
public Object eval(final Scope parentScope) {
|
public Object eval(final Scope parentScope) {
|
||||||
final ListNode formalParams = (ListNode) this.node.rest.far;
|
final ListNode<Node> formalParams = (ListNode<Node>) this.node.rest().first();
|
||||||
final ListNode body = this.node.rest.rest;
|
final ListNode<Node> body = this.node.rest().rest();
|
||||||
return new Function() {
|
|
||||||
|
return new Function<Object, Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
public Object apply(Object arg) {
|
||||||
|
Object args[] = {arg};
|
||||||
|
return this.apply(args);
|
||||||
|
}
|
||||||
|
|
||||||
public Object apply(Object... args) {
|
public Object apply(Object... args) {
|
||||||
Scope scope = new Scope(parentScope);
|
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.",
|
throw new RuntimeException(String.format("Wrong number of arguments. Expected: %s, Got: %s.",
|
||||||
formalParams.length(),
|
formalParams.length,
|
||||||
args.length));
|
args.length));
|
||||||
}
|
}
|
||||||
// Map parameter values to formal parameter names
|
// Map parameter values to formal parameter names
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Node param : formalParams) {
|
for (Node param : formalParams) {
|
||||||
SymbolNode paramSymbol = (SymbolNode) param;
|
SymbolNode paramSymbol = (SymbolNode) param;
|
||||||
scope.putValue(paramSymbol.name, args[i]);
|
scope.insertSymbol(paramSymbol.name, args[i]);
|
||||||
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;
|
package serene.simple;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
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
|
@Override
|
||||||
public Object eval(Scope scope) {
|
public Object eval(Scope scope) {
|
||||||
Function f = (Function) this.first.eval(scope);
|
Function f = (Function) this.first.eval(scope);
|
||||||
List<Object> args = new ArrayList<Object>();
|
List<Object> args = new ArrayList<Object>();
|
||||||
|
|
||||||
for (Node node : this.first) {
|
for (T node : this.rest) {
|
||||||
args.add(node.eval(scope));
|
args.add(node.eval(scope));
|
||||||
}
|
}
|
||||||
return f.apply(args.toArray());
|
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;
|
package serene.simple;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.Console;
|
import java.io.Console;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -10,19 +13,25 @@ public class Main {
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
startRepl();
|
startRepl();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
runSerene(args[0]);
|
runSerene(args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startRepl() {
|
private static void startRepl() throws IOException {
|
||||||
Scope rootScope = Scope.getRootScope();
|
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) {
|
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());
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes());
|
||||||
ListNode<Node> nodes = Reader.read(inputStream);
|
ListNode<Node> nodes = Reader.read(inputStream);
|
||||||
|
@ -37,6 +46,7 @@ public class Main {
|
||||||
System.out.println(result);
|
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;
|
package serene.simple;
|
||||||
|
|
||||||
public abstract class Node {
|
public abstract class Node {
|
||||||
|
public boolean isTruthy = true;
|
||||||
|
|
||||||
public abstract Object eval(Scope scope);
|
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.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,29 +21,31 @@ public class Reader {
|
||||||
return readNumber(inputStream);
|
return readNumber(inputStream);
|
||||||
}
|
}
|
||||||
else if (c == ')') {
|
else if (c == ')') {
|
||||||
throw new IllegalArgumentException("Unmatch paranthesis.")
|
throw new IllegalArgumentException("Unmatch paranthesis.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return readSymbol(inputStream);
|
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)));
|
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>();
|
List<Node> nodes = new ArrayList<Node>();
|
||||||
|
|
||||||
skipWhitespaces(inputStream);
|
skipWhiteSpaces(inputStream);
|
||||||
|
|
||||||
char c = inputStream.read();
|
char c = (char) inputStream.read();
|
||||||
while ((byte) c != -1) {
|
while ((byte) c != -1) {
|
||||||
inputStream.unread(c);
|
inputStream.unread(c);
|
||||||
nodes.add(readNode(inputStream));
|
nodes.add(readNode(inputStream));
|
||||||
skipWhitespaces(inputStream);
|
skipWhiteSpaces(inputStream);
|
||||||
c = (char) inputStream.read();
|
c = (char) inputStream.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ListNode.list(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Node readList(PushbackReader inputStream) throws IOException {
|
private static Node readList(PushbackReader inputStream) throws IOException {
|
||||||
|
@ -51,7 +55,7 @@ public class Reader {
|
||||||
List<Node> nodes = new ArrayList<Node>();
|
List<Node> nodes = new ArrayList<Node>();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
skipWhitespaces(inputStream);
|
skipWhiteSpaces(inputStream);
|
||||||
char c = (char) inputStream.read();
|
char c = (char) inputStream.read();
|
||||||
|
|
||||||
if (c == ')') {
|
if (c == ')') {
|
||||||
|
@ -63,6 +67,51 @@ public class Reader {
|
||||||
nodes.add(readNode(inputStream));
|
nodes.add(readNode(inputStream));
|
||||||
}
|
}
|
||||||
} while(true);
|
} 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 {
|
public class Scope {
|
||||||
private final HashMap<String, Object> symbolsMapping = new HashMap<String, Object>();
|
private final HashMap<String, Object> symbolsMapping = new HashMap<String, Object>();
|
||||||
private final Scope parent;
|
private final Scope parent;
|
||||||
|
private static final Scope root = new Scope(null);
|
||||||
|
|
||||||
public Scope() {
|
public Scope() {
|
||||||
this(null);
|
this(null);
|
||||||
|
@ -30,4 +31,8 @@ public class Scope {
|
||||||
public void insertSymbol(String symbolName, Object symbolValue) {
|
public void insertSymbol(String symbolName, Object symbolValue) {
|
||||||
this.symbolsMapping.put(symbolName, 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 IF = new SymbolNode("if");
|
||||||
private static final SymbolNode QUOTE = new SymbolNode("quote");
|
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;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Node check(ListNode l) {
|
public static Node check(ListNode<Node> l) {
|
||||||
if (l == ListNode.EMPTY) {
|
if (l == ListNode.EMPTY) {
|
||||||
return l;
|
return l;
|
||||||
} else if (l.first.equals(DEF)) {
|
} else if (l.first.equals(DEF)) {
|
||||||
|
@ -27,4 +27,9 @@ public class SpecialForm extends Node {
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object eval(Scope scope) {
|
||||||
|
throw new SereneException("Can't use SpecialForm directly");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package serene.simple;
|
package serene.simple;
|
||||||
|
|
||||||
public class SymbolNode extends Node {
|
public class SymbolNode extends Node {
|
||||||
private final String name;
|
public final String name;
|
||||||
|
|
||||||
public SymbolNode(String name) {
|
public SymbolNode(String name) {
|
||||||
this.name = 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