Bunch of new Node implementations has been added
This commit is contained in:
parent
aa16324991
commit
a5265c353e
|
@ -0,0 +1,15 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
public class DefSpecialForm extends SpecialForm {
|
||||||
|
public DefSpecialForm(ListNode listNode) {
|
||||||
|
super(listNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object eval(Scope scope) {
|
||||||
|
SymbolNode sym = (SymbolNode) this.node.rest.first;
|
||||||
|
scope.putValue(sym.name,
|
||||||
|
this.node.rest.rest.first.eval(scope));
|
||||||
|
return ListNode.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class FnSpecialForm extends SpecialForm {
|
||||||
|
public FnSpecialForm(ListNode paramsAndBody) {
|
||||||
|
super(paramsAndBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
@Override
|
||||||
|
public Object apply(Object... args) {
|
||||||
|
Scope scope = new Scope(parentScope);
|
||||||
|
if (args.length != formalParams.length()) {
|
||||||
|
throw new RuntimeException(String.format("Wrong number of arguments. Expected: %s, Got: %s.",
|
||||||
|
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]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate body
|
||||||
|
Object output = null;
|
||||||
|
for (Node node : body) {
|
||||||
|
output = node.eval(scope);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
public class FunctionNode extends Node {
|
||||||
|
@Override
|
||||||
|
public Object eval(Scope scope) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
args.add(node.eval(scope));
|
||||||
|
}
|
||||||
|
return f.apply(args.toArray());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
public abstract class Node {
|
||||||
|
public abstract Object eval(Scope scope);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
public class NumberNode extends Node {
|
||||||
|
private final Long value;
|
||||||
|
|
||||||
|
public NumberNode(Long v) {
|
||||||
|
this.value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object eval(Scope scope) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package serene.simple;
|
package serene.simple;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
|
@ -11,13 +12,13 @@ public class Reader {
|
||||||
char c = (char) inputStream.read();
|
char c = (char) inputStream.read();
|
||||||
inputStream.unread(c);
|
inputStream.unread(c);
|
||||||
|
|
||||||
if (c == "(") {
|
if (c == '(') {
|
||||||
return readList(inputStream);
|
return readList(inputStream);
|
||||||
}
|
}
|
||||||
else if (Character.isDigit(c)) {
|
else if (Character.isDigit(c)) {
|
||||||
return readNumber(inputStream);
|
return readNumber(inputStream);
|
||||||
}
|
}
|
||||||
else if (c == ")") {
|
else if (c == ')') {
|
||||||
throw new IllegalArgumentException("Unmatch paranthesis.")
|
throw new IllegalArgumentException("Unmatch paranthesis.")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -40,5 +41,28 @@ public class Reader {
|
||||||
nodes.add(readNode(inputStream));
|
nodes.add(readNode(inputStream));
|
||||||
skipWhitespaces(inputStream);
|
skipWhitespaces(inputStream);
|
||||||
c = (char) inputStream.read();
|
c = (char) inputStream.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node readList(PushbackReader inputStream) throws IOException {
|
||||||
|
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(ListNode.list(nodes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Scope {
|
||||||
|
private final HashMap<String, Object> symbolsMapping = new HashMap<String, Object>();
|
||||||
|
private final Scope parent;
|
||||||
|
|
||||||
|
public Scope() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scope(Scope parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object lookupSymbol(String symbolName) {
|
||||||
|
if (this.symbolsMapping.containsKey(symbolName)) {
|
||||||
|
return this.symbolsMapping.get(symbolName);
|
||||||
|
}
|
||||||
|
else if (this.parent != null) {
|
||||||
|
return this.parent.lookupSymbol(symbolName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new RuntimeException(String.format("Variable '%s' is not defined in this scope.",
|
||||||
|
symbolName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertSymbol(String symbolName, Object symbolValue) {
|
||||||
|
this.symbolsMapping.put(symbolName, symbolValue);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
|
||||||
|
public class SpecialForm extends Node {
|
||||||
|
private static final SymbolNode DEF = new SymbolNode("def");
|
||||||
|
private static final SymbolNode FN = new SymbolNode("fn");
|
||||||
|
private static final SymbolNode IF = new SymbolNode("if");
|
||||||
|
private static final SymbolNode QUOTE = new SymbolNode("quote");
|
||||||
|
|
||||||
|
private final ListNode node;
|
||||||
|
|
||||||
|
public SpecialForm(ListNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Node check(ListNode l) {
|
||||||
|
if (l == ListNode.EMPTY) {
|
||||||
|
return l;
|
||||||
|
} else if (l.first.equals(DEF)) {
|
||||||
|
return new DefSpecialForm(l);
|
||||||
|
} else if (l.first.equals(FN)) {
|
||||||
|
return new FnSpecialForm(l);
|
||||||
|
} else if (l.first.equals(IF)) {
|
||||||
|
return new IfSpecialForm(l);
|
||||||
|
} else if (l.first.equals(QUOTE)) {
|
||||||
|
return new QuoteSpecialForm(l);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package serene.simple;
|
||||||
|
|
||||||
|
public class SymbolNode extends Node {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public SymbolNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object eval(Scope scope) {
|
||||||
|
return scope.lookupSymbol(this.name);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue