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;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PushbackReader;
|
||||
|
@ -11,13 +12,13 @@ public class Reader {
|
|||
char c = (char) inputStream.read();
|
||||
inputStream.unread(c);
|
||||
|
||||
if (c == "(") {
|
||||
if (c == '(') {
|
||||
return readList(inputStream);
|
||||
}
|
||||
else if (Character.isDigit(c)) {
|
||||
return readNumber(inputStream);
|
||||
}
|
||||
else if (c == ")") {
|
||||
else if (c == ')') {
|
||||
throw new IllegalArgumentException("Unmatch paranthesis.")
|
||||
}
|
||||
else {
|
||||
|
@ -40,5 +41,28 @@ public class Reader {
|
|||
nodes.add(readNode(inputStream));
|
||||
skipWhitespaces(inputStream);
|
||||
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